diff options
Diffstat (limited to 'vendor/gioui.org')
419 files changed, 32012 insertions, 10033 deletions
diff --git a/vendor/gioui.org/LICENSE b/vendor/gioui.org/LICENSE index 818b5da..81f4733 100644 --- a/vendor/gioui.org/LICENSE +++ b/vendor/gioui.org/LICENSE @@ -1,5 +1,5 @@ -This project is dual-licensed under the UNLICENSE or -the MIT license with the SPDX identifier: +This project is provided under the terms of the UNLICENSE or +the MIT license denoted by the following SPDX identifier: SPDX-License-Identifier: Unlicense OR MIT diff --git a/vendor/gioui.org/app/internal/window/Gio.java b/vendor/gioui.org/app/Gio.java index 33e1a68..33e1a68 100644 --- a/vendor/gioui.org/app/internal/window/Gio.java +++ b/vendor/gioui.org/app/Gio.java diff --git a/vendor/gioui.org/app/internal/window/GioActivity.java b/vendor/gioui.org/app/GioActivity.java index 260d4b6..4565e38 100644 --- a/vendor/gioui.org/app/internal/window/GioActivity.java +++ b/vendor/gioui.org/app/GioActivity.java @@ -3,12 +3,8 @@ package org.gioui; import android.app.Activity; -import android.content.res.Configuration; -import android.os.Build; import android.os.Bundle; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; +import android.content.res.Configuration; public final class GioActivity extends Activity { private GioView view; @@ -16,13 +12,8 @@ public final class GioActivity extends Activity { @Override public void onCreate(Bundle state) { super.onCreate(state); - Window w = getWindow(); - this.view = new GioView(this); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - this.view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); - } - this.view.setLayoutParams(new WindowManager.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT)); + setContentView(view); } @@ -48,7 +39,7 @@ public final class GioActivity extends Activity { @Override public void onLowMemory() { super.onLowMemory(); - view.lowMemory(); + GioView.onLowMemory(); } @Override public void onBackPressed() { diff --git a/vendor/gioui.org/app/GioView.java b/vendor/gioui.org/app/GioView.java new file mode 100644 index 0000000..14639b0 --- /dev/null +++ b/vendor/gioui.org/app/GioView.java @@ -0,0 +1,480 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package org.gioui; + +import java.lang.Class; +import java.lang.IllegalAccessException; +import java.lang.InstantiationException; +import java.lang.ExceptionInInitializerError; +import java.lang.SecurityException; +import android.app.Activity; +import android.app.Fragment; +import android.app.FragmentManager; +import android.app.FragmentTransaction; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Rect; +import android.os.Build; +import android.os.Bundle; +import android.text.Editable; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.PointerIcon; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.WindowInsets; +import android.view.Surface; +import android.view.SurfaceView; +import android.view.SurfaceHolder; +import android.view.Window; +import android.view.WindowInsetsController; +import android.view.WindowManager; +import android.view.inputmethod.BaseInputConnection; +import android.view.inputmethod.InputConnection; +import android.view.inputmethod.InputMethodManager; +import android.view.inputmethod.EditorInfo; +import android.text.InputType; +import android.view.accessibility.AccessibilityNodeProvider; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; + +import java.io.UnsupportedEncodingException; + +public final class GioView extends SurfaceView { + private static boolean jniLoaded; + + private final SurfaceHolder.Callback surfCallbacks; + private final View.OnFocusChangeListener focusCallback; + private final InputMethodManager imm; + private final float scrollXScale; + private final float scrollYScale; + private int keyboardHint; + private AccessibilityManager accessManager; + + private long nhandle; + + public GioView(Context context) { + this(context, null); + } + + public GioView(Context context, AttributeSet attrs) { + super(context, attrs); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); + } + setLayoutParams(new WindowManager.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT)); + + // Late initialization of the Go runtime to wait for a valid context. + Gio.init(context.getApplicationContext()); + + // Set background color to transparent to avoid a flickering + // issue on ChromeOS. + setBackgroundColor(Color.argb(0, 0, 0, 0)); + + ViewConfiguration conf = ViewConfiguration.get(context); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + scrollXScale = conf.getScaledHorizontalScrollFactor(); + scrollYScale = conf.getScaledVerticalScrollFactor(); + + // The platform focus highlight is not aware of Gio's widgets. + setDefaultFocusHighlightEnabled(false); + } else { + float listItemHeight = 48; // dp + float px = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + listItemHeight, + getResources().getDisplayMetrics() + ); + scrollXScale = px; + scrollYScale = px; + } + + accessManager = (AccessibilityManager)context.getSystemService(Context.ACCESSIBILITY_SERVICE); + nhandle = onCreateView(this); + imm = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE); + setFocusable(true); + setFocusableInTouchMode(true); + focusCallback = new View.OnFocusChangeListener() { + @Override public void onFocusChange(View v, boolean focus) { + GioView.this.onFocusChange(nhandle, focus); + } + }; + setOnFocusChangeListener(focusCallback); + surfCallbacks = new SurfaceHolder.Callback() { + @Override public void surfaceCreated(SurfaceHolder holder) { + // Ignore; surfaceChanged is guaranteed to be called immediately after this. + } + @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + onSurfaceChanged(nhandle, getHolder().getSurface()); + } + @Override public void surfaceDestroyed(SurfaceHolder holder) { + onSurfaceDestroyed(nhandle); + } + }; + getHolder().addCallback(surfCallbacks); + } + + @Override public boolean onKeyDown(int keyCode, KeyEvent event) { + if (nhandle != 0) { + onKeyEvent(nhandle, keyCode, event.getUnicodeChar(), event.getEventTime()); + } + return false; + } + + @Override public boolean onGenericMotionEvent(MotionEvent event) { + dispatchMotionEvent(event); + return true; + } + + @Override public boolean onTouchEvent(MotionEvent event) { + // Ask for unbuffered events. Flutter and Chrome do it + // so assume it's good for us as well. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + requestUnbufferedDispatch(event); + } + + dispatchMotionEvent(event); + return true; + } + + private void setCursor(int id) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { + return; + } + PointerIcon pointerIcon = PointerIcon.getSystemIcon(getContext(), id); + setPointerIcon(pointerIcon); + } + + private void setOrientation(int id, int fallback) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { + id = fallback; + } + ((Activity) this.getContext()).setRequestedOrientation(id); + } + + private void setFullscreen(boolean enabled) { + int flags = this.getSystemUiVisibility(); + if (enabled) { + flags |= SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + flags |= SYSTEM_UI_FLAG_HIDE_NAVIGATION; + flags |= SYSTEM_UI_FLAG_FULLSCREEN; + flags |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + } else { + flags &= ~SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + flags &= ~SYSTEM_UI_FLAG_HIDE_NAVIGATION; + flags &= ~SYSTEM_UI_FLAG_FULLSCREEN; + flags &= ~SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + } + this.setSystemUiVisibility(flags); + } + + private enum Bar { + NAVIGATION, + STATUS, + } + + private void setBarColor(Bar t, int color, int luminance) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + return; + } + + Window window = ((Activity) this.getContext()).getWindow(); + + int insetsMask; + int viewMask; + + switch (t) { + case STATUS: + insetsMask = WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; + viewMask = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; + window.setStatusBarColor(color); + break; + case NAVIGATION: + insetsMask = WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; + viewMask = View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; + window.setNavigationBarColor(color); + break; + default: + throw new RuntimeException("invalid bar type"); + } + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + return; + } + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + int flags = this.getSystemUiVisibility(); + if (luminance > 128) { + flags |= viewMask; + } else { + flags &= ~viewMask; + } + this.setSystemUiVisibility(flags); + return; + } + + WindowInsetsController insetsController = window.getInsetsController(); + if (insetsController == null) { + return; + } + if (luminance > 128) { + insetsController.setSystemBarsAppearance(insetsMask, insetsMask); + } else { + insetsController.setSystemBarsAppearance(0, insetsMask); + } + } + + private void setStatusColor(int color, int luminance) { + this.setBarColor(Bar.STATUS, color, luminance); + } + + private void setNavigationColor(int color, int luminance) { + this.setBarColor(Bar.NAVIGATION, color, luminance); + } + + @Override protected boolean dispatchHoverEvent(MotionEvent event) { + if (!accessManager.isTouchExplorationEnabled()) { + return super.dispatchHoverEvent(event); + } + switch (event.getAction()) { + case MotionEvent.ACTION_HOVER_ENTER: + // Fall through. + case MotionEvent.ACTION_HOVER_MOVE: + onTouchExploration(nhandle, event.getX(), event.getY()); + break; + case MotionEvent.ACTION_HOVER_EXIT: + onExitTouchExploration(nhandle); + break; + } + return true; + } + + void sendA11yEvent(int eventType, int viewId) { + if (!accessManager.isEnabled()) { + return; + } + AccessibilityEvent event = obtainA11yEvent(eventType, viewId); + getParent().requestSendAccessibilityEvent(this, event); + } + + AccessibilityEvent obtainA11yEvent(int eventType, int viewId) { + AccessibilityEvent event = AccessibilityEvent.obtain(eventType); + event.setPackageName(getContext().getPackageName()); + event.setSource(this, viewId); + return event; + } + + boolean isA11yActive() { + return accessManager.isEnabled(); + } + + void sendA11yChange(int viewId) { + if (!accessManager.isEnabled()) { + return; + } + AccessibilityEvent event = obtainA11yEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED, viewId); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE); + } + getParent().requestSendAccessibilityEvent(this, event); + } + + private void dispatchMotionEvent(MotionEvent event) { + if (nhandle == 0) { + return; + } + for (int j = 0; j < event.getHistorySize(); j++) { + long time = event.getHistoricalEventTime(j); + for (int i = 0; i < event.getPointerCount(); i++) { + onTouchEvent( + nhandle, + event.ACTION_MOVE, + event.getPointerId(i), + event.getToolType(i), + event.getHistoricalX(i, j), + event.getHistoricalY(i, j), + scrollXScale*event.getHistoricalAxisValue(MotionEvent.AXIS_HSCROLL, i, j), + scrollYScale*event.getHistoricalAxisValue(MotionEvent.AXIS_VSCROLL, i, j), + event.getButtonState(), + time); + } + } + int act = event.getActionMasked(); + int idx = event.getActionIndex(); + for (int i = 0; i < event.getPointerCount(); i++) { + int pact = event.ACTION_MOVE; + if (i == idx) { + pact = act; + } + onTouchEvent( + nhandle, + pact, + event.getPointerId(i), + event.getToolType(i), + event.getX(i), event.getY(i), + scrollXScale*event.getAxisValue(MotionEvent.AXIS_HSCROLL, i), + scrollYScale*event.getAxisValue(MotionEvent.AXIS_VSCROLL, i), + event.getButtonState(), + event.getEventTime()); + } + } + + @Override public InputConnection onCreateInputConnection(EditorInfo editor) { + editor.inputType = this.keyboardHint; + editor.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN | EditorInfo.IME_FLAG_NO_EXTRACT_UI; + return new InputConnection(this); + } + + void setInputHint(int hint) { + if (hint == this.keyboardHint) { + return; + } + this.keyboardHint = hint; + imm.restartInput(this); + } + + void showTextInput() { + GioView.this.requestFocus(); + imm.showSoftInput(GioView.this, 0); + } + + void hideTextInput() { + imm.hideSoftInputFromWindow(getWindowToken(), 0); + } + + @Override protected boolean fitSystemWindows(Rect insets) { + if (nhandle != 0) { + onWindowInsets(nhandle, insets.top, insets.right, insets.bottom, insets.left); + } + return true; + } + + @Override protected void onDraw(Canvas canvas) { + if (nhandle != 0) { + onFrameCallback(nhandle); + } + } + + int getDensity() { + return getResources().getDisplayMetrics().densityDpi; + } + + float getFontScale() { + return getResources().getConfiguration().fontScale; + } + + public void start() { + if (nhandle != 0) { + onStartView(nhandle); + } + } + + public void stop() { + if (nhandle != 0) { + onStopView(nhandle); + } + } + + public void destroy() { + if (nhandle != 0) { + onDestroyView(nhandle); + } + } + + protected void unregister() { + setOnFocusChangeListener(null); + getHolder().removeCallback(surfCallbacks); + nhandle = 0; + } + + public void configurationChanged() { + if (nhandle != 0) { + onConfigurationChanged(nhandle); + } + } + + public boolean backPressed() { + if (nhandle == 0) { + return false; + } + return onBack(nhandle); + } + + static private native long onCreateView(GioView view); + static private native void onDestroyView(long handle); + static private native void onStartView(long handle); + static private native void onStopView(long handle); + static private native void onSurfaceDestroyed(long handle); + static private native void onSurfaceChanged(long handle, Surface surface); + static private native void onConfigurationChanged(long handle); + static private native void onWindowInsets(long handle, int top, int right, int bottom, int left); + static public native void onLowMemory(); + static private native void onTouchEvent(long handle, int action, int pointerID, int tool, float x, float y, float scrollX, float scrollY, int buttons, long time); + static private native void onKeyEvent(long handle, int code, int character, long time); + static private native void onFrameCallback(long handle); + static private native boolean onBack(long handle); + static private native void onFocusChange(long handle, boolean focus); + static private native AccessibilityNodeInfo initializeAccessibilityNodeInfo(long handle, int viewId, int screenX, int screenY, AccessibilityNodeInfo info); + static private native void onTouchExploration(long handle, float x, float y); + static private native void onExitTouchExploration(long handle); + static private native void onA11yFocus(long handle, int viewId); + static private native void onClearA11yFocus(long handle, int viewId); + + private static class InputConnection extends BaseInputConnection { + private final Editable editable; + + InputConnection(View view) { + // Passing false enables "dummy mode", where the BaseInputConnection + // attempts to convert IME operations to key events. + super(view, false); + editable = Editable.Factory.getInstance().newEditable(""); + } + + @Override public Editable getEditable() { + return editable; + } + } + + @Override public AccessibilityNodeProvider getAccessibilityNodeProvider() { + return new AccessibilityNodeProvider() { + private final int[] screenOff = new int[2]; + + @Override public AccessibilityNodeInfo createAccessibilityNodeInfo(int viewId) { + AccessibilityNodeInfo info = null; + if (viewId == View.NO_ID) { + info = AccessibilityNodeInfo.obtain(GioView.this); + GioView.this.onInitializeAccessibilityNodeInfo(info); + } else { + info = AccessibilityNodeInfo.obtain(GioView.this, viewId); + info.setPackageName(getContext().getPackageName()); + info.setVisibleToUser(true); + } + GioView.this.getLocationOnScreen(screenOff); + info = GioView.this.initializeAccessibilityNodeInfo(nhandle, viewId, screenOff[0], screenOff[1], info); + return info; + } + + @Override public boolean performAction(int viewId, int action, Bundle arguments) { + if (viewId == View.NO_ID) { + return GioView.this.performAccessibilityAction(action, arguments); + } + switch (action) { + case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: + GioView.this.onA11yFocus(nhandle, viewId); + GioView.this.sendA11yEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, viewId); + return true; + case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: + GioView.this.onClearA11yFocus(nhandle, viewId); + GioView.this.sendA11yEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED, viewId); + return true; + } + return false; + } + }; + } +} diff --git a/vendor/gioui.org/app/app.go b/vendor/gioui.org/app/app.go index d21224e..e00298a 100644 --- a/vendor/gioui.org/app/app.go +++ b/vendor/gioui.org/app/app.go @@ -5,8 +5,6 @@ package app import ( "os" "strings" - - "gioui.org/app/internal/window" ) // extraArgs contains extra arguments to append to @@ -44,5 +42,5 @@ func DataDir() (string, error) { // require control of the main thread of the program for // running windows. func Main() { - window.Main() + osMain() } diff --git a/vendor/gioui.org/app/app_android.go b/vendor/gioui.org/app/app_android.go deleted file mode 100644 index 58e40d0..0000000 --- a/vendor/gioui.org/app/app_android.go +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package app - -import ( - "gioui.org/app/internal/window" -) - -// JavaVM returns the global JNI JavaVM. -func JavaVM() uintptr { - return window.JavaVM() -} - -// AppContext returns the global Application context as a JNI -// jobject. -func AppContext() uintptr { - return window.AppContext() -} - -// Do invokes the function with a JNI jobject handle to the underlying -// Android View. The function is invoked on the main thread, and the -// handle is invalidated after the function returns. -// -// Note: Do may deadlock if called from the same goroutine that receives from -// Events. -func (w *Window) Do(f func(view uintptr)) { - type androidDriver interface { - Do(f func(view uintptr)) bool - } - success := make(chan bool) - for { - driver := make(chan androidDriver, 1) - // two-stage process: first wait for a valid driver... - w.driverDo(func() { - driver <- w.driver.(androidDriver) - }) - // .. then run the function on the main thread using the - // driver. The driver Do method returns false if the - // view was invalidated while switching to the main thread. - window.RunOnMain(func() { - d := <-driver - success <- d.Do(f) - }) - if <-success { - break - } - } -} diff --git a/vendor/gioui.org/app/d3d11_windows.go b/vendor/gioui.org/app/d3d11_windows.go new file mode 100644 index 0000000..ab04ee4 --- /dev/null +++ b/vendor/gioui.org/app/d3d11_windows.go @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package app + +import ( + "fmt" + "unsafe" + + "gioui.org/gpu" + "gioui.org/internal/d3d11" +) + +type d3d11Context struct { + win *window + dev *d3d11.Device + ctx *d3d11.DeviceContext + + swchain *d3d11.IDXGISwapChain + renderTarget *d3d11.RenderTargetView + width, height int +} + +const debug = false + +func init() { + drivers = append(drivers, gpuAPI{ + priority: 1, + initializer: func(w *window) (context, error) { + hwnd, _, _ := w.HWND() + var flags uint32 + if debug { + flags |= d3d11.CREATE_DEVICE_DEBUG + } + dev, ctx, _, err := d3d11.CreateDevice( + d3d11.DRIVER_TYPE_HARDWARE, + flags, + ) + if err != nil { + return nil, fmt.Errorf("NewContext: %v", err) + } + swchain, err := d3d11.CreateSwapChain(dev, hwnd) + if err != nil { + d3d11.IUnknownRelease(unsafe.Pointer(ctx), ctx.Vtbl.Release) + d3d11.IUnknownRelease(unsafe.Pointer(dev), dev.Vtbl.Release) + return nil, err + } + return &d3d11Context{win: w, dev: dev, ctx: ctx, swchain: swchain}, nil + }, + }) +} + +func (c *d3d11Context) API() gpu.API { + return gpu.Direct3D11{Device: unsafe.Pointer(c.dev)} +} + +func (c *d3d11Context) RenderTarget() (gpu.RenderTarget, error) { + return gpu.Direct3D11RenderTarget{ + RenderTarget: unsafe.Pointer(c.renderTarget), + }, nil +} + +func (c *d3d11Context) Present() error { + err := c.swchain.Present(1, 0) + if err == nil { + return nil + } + if err, ok := err.(d3d11.ErrorCode); ok { + switch err.Code { + case d3d11.DXGI_STATUS_OCCLUDED: + // Ignore + return nil + case d3d11.DXGI_ERROR_DEVICE_RESET, d3d11.DXGI_ERROR_DEVICE_REMOVED, d3d11.D3DDDIERR_DEVICEREMOVED: + return gpu.ErrDeviceLost + } + } + return err +} + +func (c *d3d11Context) Refresh() error { + var width, height int + _, width, height = c.win.HWND() + if c.renderTarget != nil && width == c.width && height == c.height { + return nil + } + c.releaseFBO() + if err := c.swchain.ResizeBuffers(0, 0, 0, d3d11.DXGI_FORMAT_UNKNOWN, 0); err != nil { + return err + } + c.width = width + c.height = height + + backBuffer, err := c.swchain.GetBuffer(0, &d3d11.IID_Texture2D) + if err != nil { + return err + } + texture := (*d3d11.Resource)(unsafe.Pointer(backBuffer)) + renderTarget, err := c.dev.CreateRenderTargetView(texture) + d3d11.IUnknownRelease(unsafe.Pointer(backBuffer), backBuffer.Vtbl.Release) + if err != nil { + return err + } + c.renderTarget = renderTarget + return nil +} + +func (c *d3d11Context) Lock() error { + c.ctx.OMSetRenderTargets(c.renderTarget, nil) + return nil +} + +func (c *d3d11Context) Unlock() {} + +func (c *d3d11Context) Release() { + c.releaseFBO() + if c.swchain != nil { + d3d11.IUnknownRelease(unsafe.Pointer(c.swchain), c.swchain.Vtbl.Release) + } + if c.ctx != nil { + d3d11.IUnknownRelease(unsafe.Pointer(c.ctx), c.ctx.Vtbl.Release) + } + if c.dev != nil { + d3d11.IUnknownRelease(unsafe.Pointer(c.dev), c.dev.Vtbl.Release) + } + *c = d3d11Context{} + if debug { + d3d11.ReportLiveObjects() + } +} + +func (c *d3d11Context) releaseFBO() { + if c.renderTarget != nil { + d3d11.IUnknownRelease(unsafe.Pointer(c.renderTarget), c.renderTarget.Vtbl.Release) + c.renderTarget = nil + } +} diff --git a/vendor/gioui.org/app/datadir.go b/vendor/gioui.org/app/datadir.go index 31e5453..500a59a 100644 --- a/vendor/gioui.org/app/datadir.go +++ b/vendor/gioui.org/app/datadir.go @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Unlicense OR MIT +//go:build !android // +build !android package app diff --git a/vendor/gioui.org/app/datadir_android.go b/vendor/gioui.org/app/datadir_android.go deleted file mode 100644 index 450e6cf..0000000 --- a/vendor/gioui.org/app/datadir_android.go +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -// +build android - -package app - -import "C" - -import ( - "os" - "path/filepath" - "sync" - - "gioui.org/app/internal/window" -) - -var ( - dataDirOnce sync.Once - dataPath string -) - -func dataDir() (string, error) { - dataDirOnce.Do(func() { - dataPath = window.GetDataDir() - // Set XDG_CACHE_HOME to make os.UserCacheDir work. - if _, exists := os.LookupEnv("XDG_CACHE_HOME"); !exists { - cachePath := filepath.Join(dataPath, "cache") - os.Setenv("XDG_CACHE_HOME", cachePath) - } - // Set XDG_CONFIG_HOME to make os.UserConfigDir work. - if _, exists := os.LookupEnv("XDG_CONFIG_HOME"); !exists { - cfgPath := filepath.Join(dataPath, "config") - os.Setenv("XDG_CONFIG_HOME", cfgPath) - } - // Set HOME to make os.UserHomeDir work. - if _, exists := os.LookupEnv("HOME"); !exists { - os.Setenv("HOME", dataPath) - } - }) - return dataPath, nil -} diff --git a/vendor/gioui.org/app/egl_android.go b/vendor/gioui.org/app/egl_android.go new file mode 100644 index 0000000..2c31130 --- /dev/null +++ b/vendor/gioui.org/app/egl_android.go @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package app + +/* +#include <android/native_window_jni.h> +#include <EGL/egl.h> +*/ +import "C" + +import ( + "unsafe" + + "gioui.org/internal/egl" +) + +type androidContext struct { + win *window + eglSurf egl.NativeWindowType + width, height int + *egl.Context +} + +func init() { + newAndroidGLESContext = func(w *window) (context, error) { + ctx, err := egl.NewContext(nil) + if err != nil { + return nil, err + } + return &androidContext{win: w, Context: ctx}, nil + } +} + +func (c *androidContext) Release() { + if c.Context != nil { + c.Context.Release() + c.Context = nil + } +} + +func (c *androidContext) Refresh() error { + c.Context.ReleaseSurface() + if err := c.win.setVisual(c.Context.VisualID()); err != nil { + return err + } + win, width, height := c.win.nativeWindow() + c.eglSurf = egl.NativeWindowType(unsafe.Pointer(win)) + c.width, c.height = width, height + return nil +} + +func (c *androidContext) Lock() error { + // The Android emulator creates a broken surface if it is not + // created on the same thread as the context is made current. + if c.eglSurf != nil { + if err := c.Context.CreateSurface(c.eglSurf, c.width, c.height); err != nil { + return err + } + c.eglSurf = nil + } + return c.Context.MakeCurrent() +} + +func (c *androidContext) Unlock() { + c.Context.ReleaseCurrent() +} diff --git a/vendor/gioui.org/app/internal/window/egl_wayland.go b/vendor/gioui.org/app/egl_wayland.go index b4878b4..c2406a0 100644 --- a/vendor/gioui.org/app/internal/window/egl_wayland.go +++ b/vendor/gioui.org/app/egl_wayland.go @@ -1,20 +1,22 @@ // SPDX-License-Identifier: Unlicense OR MIT -// +build linux,!android,!nowayland freebsd +//go:build ((linux && !android) || freebsd) && !nowayland +// +build linux,!android freebsd +// +build !nowayland -package window +package app import ( "errors" "unsafe" - "gioui.org/app/internal/egl" + "gioui.org/internal/egl" ) /* #cgo linux pkg-config: egl wayland-egl #cgo freebsd openbsd LDFLAGS: -lwayland-egl -#cgo CFLAGS: -DMESA_EGL_NO_X11_HEADERS +#cgo CFLAGS: -DEGL_NO_X11 #include <EGL/egl.h> #include <wayland-client.h> @@ -22,22 +24,24 @@ import ( */ import "C" -type context struct { +type wlContext struct { win *window *egl.Context eglWin *C.struct_wl_egl_window } -func (w *window) NewContext() (Context, error) { - disp := egl.NativeDisplayType(unsafe.Pointer(w.display())) - ctx, err := egl.NewContext(disp) - if err != nil { - return nil, err +func init() { + newWaylandEGLContext = func(w *window) (context, error) { + disp := egl.NativeDisplayType(unsafe.Pointer(w.display())) + ctx, err := egl.NewContext(disp) + if err != nil { + return nil, err + } + return &wlContext{Context: ctx, win: w}, nil } - return &context{Context: ctx, win: w}, nil } -func (c *context) Release() { +func (c *wlContext) Release() { if c.Context != nil { c.Context.Release() c.Context = nil @@ -48,7 +52,7 @@ func (c *context) Release() { } } -func (c *context) MakeCurrent() error { +func (c *wlContext) Refresh() error { c.Context.ReleaseSurface() if c.eglWin != nil { C.wl_egl_window_destroy(c.eglWin) @@ -64,12 +68,13 @@ func (c *context) MakeCurrent() error { } c.eglWin = eglWin eglSurf := egl.NativeWindowType(uintptr(unsafe.Pointer(eglWin))) - if err := c.Context.CreateSurface(eglSurf, width, height); err != nil { - return err - } - return c.Context.MakeCurrent() + return c.Context.CreateSurface(eglSurf, width, height) } -func (c *context) Lock() {} +func (c *wlContext) Lock() error { + return c.Context.MakeCurrent() +} -func (c *context) Unlock() {} +func (c *wlContext) Unlock() { + c.Context.ReleaseCurrent() +} diff --git a/vendor/gioui.org/app/internal/window/egl_windows.go b/vendor/gioui.org/app/egl_windows.go index 654d279..3a95450 100644 --- a/vendor/gioui.org/app/internal/window/egl_windows.go +++ b/vendor/gioui.org/app/egl_windows.go @@ -1,9 +1,11 @@ // SPDX-License-Identifier: Unlicense OR MIT -package window +package app import ( - "gioui.org/app/internal/egl" + "golang.org/x/sys/windows" + + "gioui.org/internal/egl" ) type glContext struct { @@ -12,9 +14,9 @@ type glContext struct { } func init() { - backends = append(backends, gpuAPI{ + drivers = append(drivers, gpuAPI{ priority: 2, - initializer: func(w *window) (Context, error) { + initializer: func(w *window) (context, error) { disp := egl.NativeDisplayType(w.HDC()) ctx, err := egl.NewContext(disp) if err != nil { @@ -32,9 +34,13 @@ func (c *glContext) Release() { } } -func (c *glContext) MakeCurrent() error { +func (c *glContext) Refresh() error { c.Context.ReleaseSurface() - win, width, height := c.win.HWND() + var ( + win windows.Handle + width, height int + ) + win, width, height = c.win.HWND() eglSurf := egl.NativeWindowType(win) if err := c.Context.CreateSurface(eglSurf, width, height); err != nil { return err @@ -43,9 +49,14 @@ func (c *glContext) MakeCurrent() error { return err } c.Context.EnableVSync(true) + c.Context.ReleaseCurrent() return nil } -func (c *glContext) Lock() {} +func (c *glContext) Lock() error { + return c.Context.MakeCurrent() +} -func (c *glContext) Unlock() {} +func (c *glContext) Unlock() { + c.Context.ReleaseCurrent() +} diff --git a/vendor/gioui.org/app/egl_x11.go b/vendor/gioui.org/app/egl_x11.go new file mode 100644 index 0000000..7234808 --- /dev/null +++ b/vendor/gioui.org/app/egl_x11.go @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build ((linux && !android) || freebsd || openbsd) && !nox11 +// +build linux,!android freebsd openbsd +// +build !nox11 + +package app + +import ( + "unsafe" + + "gioui.org/internal/egl" +) + +type x11Context struct { + win *x11Window + *egl.Context +} + +func init() { + newX11EGLContext = func(w *x11Window) (context, error) { + disp := egl.NativeDisplayType(unsafe.Pointer(w.display())) + ctx, err := egl.NewContext(disp) + if err != nil { + return nil, err + } + return &x11Context{win: w, Context: ctx}, nil + } +} + +func (c *x11Context) Release() { + if c.Context != nil { + c.Context.Release() + c.Context = nil + } +} + +func (c *x11Context) Refresh() error { + c.Context.ReleaseSurface() + win, width, height := c.win.window() + eglSurf := egl.NativeWindowType(uintptr(win)) + if err := c.Context.CreateSurface(eglSurf, width, height); err != nil { + return err + } + if err := c.Context.MakeCurrent(); err != nil { + return err + } + c.Context.EnableVSync(true) + c.Context.ReleaseCurrent() + return nil +} + +func (c *x11Context) Lock() error { + return c.Context.MakeCurrent() +} + +func (c *x11Context) Unlock() { + c.Context.ReleaseCurrent() +} diff --git a/vendor/gioui.org/app/internal/window/framework_ios.h b/vendor/gioui.org/app/framework_ios.h index 18e5a02..18e5a02 100644 --- a/vendor/gioui.org/app/internal/window/framework_ios.h +++ b/vendor/gioui.org/app/framework_ios.h diff --git a/vendor/gioui.org/app/internal/window/gl_ios.go b/vendor/gioui.org/app/gl_ios.go index 0099189..2dc2dbf 100644 --- a/vendor/gioui.org/app/internal/window/gl_ios.go +++ b/vendor/gioui.org/app/gl_ios.go @@ -1,10 +1,13 @@ // SPDX-License-Identifier: Unlicense OR MIT -// +build darwin,ios +//go:build darwin && ios && nometal +// +build darwin,ios,nometal -package window +package app /* +@import UIKit; + #include <CoreFoundation/CoreFoundation.h> #include <OpenGLES/ES2/gl.h> #include <OpenGLES/ES2/glext.h> @@ -14,32 +17,34 @@ __attribute__ ((visibility ("hidden"))) int gio_presentRenderbuffer(CFTypeRef ct __attribute__ ((visibility ("hidden"))) int gio_makeCurrent(CFTypeRef ctx); __attribute__ ((visibility ("hidden"))) CFTypeRef gio_createContext(void); __attribute__ ((visibility ("hidden"))) CFTypeRef gio_createGLLayer(void); + +static CFTypeRef getViewLayer(CFTypeRef viewRef) { + @autoreleasepool { + UIView *view = (__bridge UIView *)viewRef; + return CFBridgingRetain(view.layer); + } +} + */ import "C" import ( "errors" "fmt" + "runtime" - "gioui.org/app/internal/glimpl" - "gioui.org/gpu/backend" - "gioui.org/gpu/gl" + "gioui.org/gpu" + "gioui.org/internal/gl" ) type context struct { - owner *window - c *glimpl.Functions - ctx C.CFTypeRef - layer C.CFTypeRef - init bool - frameBuffer gl.Framebuffer - colorBuffer, depthBuffer gl.Renderbuffer -} - -func init() { - layerFactory = func() uintptr { - return uintptr(C.gio_createGLLayer()) - } + owner *window + c *gl.Functions + ctx C.CFTypeRef + layer C.CFTypeRef + init bool + frameBuffer gl.Framebuffer + colorBuffer gl.Renderbuffer } func newContext(w *window) (*context, error) { @@ -47,17 +52,30 @@ func newContext(w *window) (*context, error) { if ctx == 0 { return nil, fmt.Errorf("failed to create EAGLContext") } + api := contextAPI() + f, err := gl.NewFunctions(api.Context, api.ES) + if err != nil { + return nil, err + } c := &context{ ctx: ctx, owner: w, - layer: C.CFTypeRef(w.contextLayer()), - c: new(glimpl.Functions), + layer: C.getViewLayer(w.contextView()), + c: f, } return c, nil } -func (c *context) Backend() (backend.Device, error) { - return gl.NewBackend(c.c) +func contextAPI() gpu.OpenGL { + return gpu.OpenGL{} +} + +func (c *context) RenderTarget() gpu.RenderTarget { + return gpu.OpenGLRenderTarget(c.frameBuffer) +} + +func (c *context) API() gpu.API { + return contextAPI() } func (c *context) Release() { @@ -67,7 +85,6 @@ func (c *context) Release() { C.gio_renderbufferStorage(c.ctx, 0, C.GLenum(gl.RENDERBUFFER)) c.c.DeleteFramebuffer(c.frameBuffer) c.c.DeleteRenderbuffer(c.colorBuffer) - c.c.DeleteRenderbuffer(c.depthBuffer) C.gio_makeCurrent(0) C.CFRelease(c.ctx) c.ctx = 0 @@ -77,10 +94,6 @@ func (c *context) Present() error { if c.layer == 0 { panic("context is not active") } - // Discard depth buffer as recommended in - // https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/WorkingwithEAGLContexts/WorkingwithEAGLContexts.html - c.c.BindFramebuffer(gl.FRAMEBUFFER, c.frameBuffer) - c.c.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT) c.c.BindRenderbuffer(gl.RENDERBUFFER, c.colorBuffer) if C.gio_presentRenderbuffer(c.ctx, C.GLenum(gl.RENDERBUFFER)) == 0 { return errors.New("presentRenderBuffer failed") @@ -88,23 +101,30 @@ func (c *context) Present() error { return nil } -func (c *context) Lock() {} +func (c *context) Lock() error { + // OpenGL contexts are implicit and thread-local. Lock the OS thread. + runtime.LockOSThread() -func (c *context) Unlock() {} + if C.gio_makeCurrent(c.ctx) == 0 { + return errors.New("[EAGLContext setCurrentContext] failed") + } + return nil +} + +func (c *context) Unlock() { + C.gio_makeCurrent(0) +} -func (c *context) MakeCurrent() error { +func (c *context) Refresh() error { if C.gio_makeCurrent(c.ctx) == 0 { - C.CFRelease(c.ctx) - c.ctx = 0 return errors.New("[EAGLContext setCurrentContext] failed") } if !c.init { c.init = true c.frameBuffer = c.c.CreateFramebuffer() c.colorBuffer = c.c.CreateRenderbuffer() - c.depthBuffer = c.c.CreateRenderbuffer() } - if !c.owner.isVisible() { + if !c.owner.visible { // Make sure any in-flight GL commands are complete. c.c.Finish() return nil @@ -114,14 +134,9 @@ func (c *context) MakeCurrent() error { if C.gio_renderbufferStorage(c.ctx, c.layer, C.GLenum(gl.RENDERBUFFER)) == 0 { return errors.New("renderbufferStorage failed") } - w := c.c.GetRenderbufferParameteri(gl.RENDERBUFFER, gl.RENDERBUFFER_WIDTH) - h := c.c.GetRenderbufferParameteri(gl.RENDERBUFFER, gl.RENDERBUFFER_HEIGHT) - c.c.BindRenderbuffer(gl.RENDERBUFFER, c.depthBuffer) - c.c.RenderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, w, h) c.c.BindRenderbuffer(gl.RENDERBUFFER, currentRB) c.c.BindFramebuffer(gl.FRAMEBUFFER, c.frameBuffer) c.c.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, c.colorBuffer) - c.c.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, c.depthBuffer) if st := c.c.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE { return fmt.Errorf("framebuffer incomplete, status: %#x\n", st) } diff --git a/vendor/gioui.org/app/internal/window/gl_ios.m b/vendor/gioui.org/app/gl_ios.m index 065ea97..8e50753 100644 --- a/vendor/gioui.org/app/internal/window/gl_ios.m +++ b/vendor/gioui.org/app/gl_ios.m @@ -1,12 +1,16 @@ // SPDX-License-Identifier: Unlicense OR MIT -// +build darwin,ios +// +build darwin,ios,nometal @import UIKit; @import OpenGLES; #include "_cgo_export.h" +Class gio_layerClass(void) { + return [CAEAGLLayer class]; +} + int gio_renderbufferStorage(CFTypeRef ctxRef, CFTypeRef layerRef, GLenum buffer) { EAGLContext *ctx = (__bridge EAGLContext *)ctxRef; CAEAGLLayer *layer = (__bridge CAEAGLLayer *)layerRef; diff --git a/vendor/gioui.org/app/gl_js.go b/vendor/gioui.org/app/gl_js.go new file mode 100644 index 0000000..e3aee8d --- /dev/null +++ b/vendor/gioui.org/app/gl_js.go @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package app + +import ( + "errors" + "syscall/js" + + "gioui.org/gpu" + "gioui.org/internal/gl" +) + +type glContext struct { + ctx js.Value + cnv js.Value +} + +func newContext(w *window) (*glContext, error) { + args := map[string]interface{}{ + // Enable low latency rendering. + // See https://developers.google.com/web/updates/2019/05/desynchronized. + "desynchronized": true, + "preserveDrawingBuffer": true, + } + ctx := w.cnv.Call("getContext", "webgl2", args) + if ctx.IsNull() { + ctx = w.cnv.Call("getContext", "webgl", args) + } + if ctx.IsNull() { + return nil, errors.New("app: webgl is not supported") + } + c := &glContext{ + ctx: ctx, + cnv: w.cnv, + } + return c, nil +} + +func (c *glContext) RenderTarget() (gpu.RenderTarget, error) { + return gpu.OpenGLRenderTarget{}, nil +} + +func (c *glContext) API() gpu.API { + return gpu.OpenGL{Context: gl.Context(c.ctx)} +} + +func (c *glContext) Release() { +} + +func (c *glContext) Present() error { + if c.ctx.Call("isContextLost").Bool() { + return errors.New("context lost") + } + return nil +} + +func (c *glContext) Lock() error { + return nil +} + +func (c *glContext) Unlock() {} + +func (c *glContext) Refresh() error { + return nil +} + +func (w *window) NewContext() (context, error) { + return newContext(w) +} diff --git a/vendor/gioui.org/app/gl_macos.go b/vendor/gioui.org/app/gl_macos.go new file mode 100644 index 0000000..7511bbd --- /dev/null +++ b/vendor/gioui.org/app/gl_macos.go @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build darwin && !ios && nometal +// +build darwin,!ios,nometal + +package app + +import ( + "errors" + "runtime" + + "unsafe" + + "gioui.org/gpu" + "gioui.org/internal/gl" +) + +/* +#include <CoreFoundation/CoreFoundation.h> +#include <CoreGraphics/CoreGraphics.h> +#include <AppKit/AppKit.h> +#include <dlfcn.h> + +__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createGLContext(void); +__attribute__ ((visibility ("hidden"))) void gio_setContextView(CFTypeRef ctx, CFTypeRef view); +__attribute__ ((visibility ("hidden"))) void gio_makeCurrentContext(CFTypeRef ctx); +__attribute__ ((visibility ("hidden"))) void gio_updateContext(CFTypeRef ctx); +__attribute__ ((visibility ("hidden"))) void gio_flushContextBuffer(CFTypeRef ctx); +__attribute__ ((visibility ("hidden"))) void gio_clearCurrentContext(void); +__attribute__ ((visibility ("hidden"))) void gio_lockContext(CFTypeRef ctxRef); +__attribute__ ((visibility ("hidden"))) void gio_unlockContext(CFTypeRef ctxRef); + +typedef void (*PFN_glFlush)(void); + +static void glFlush(PFN_glFlush f) { + f(); +} +*/ +import "C" + +type glContext struct { + c *gl.Functions + ctx C.CFTypeRef + view C.CFTypeRef + + glFlush C.PFN_glFlush +} + +func newContext(w *window) (*glContext, error) { + clib := C.CString("/System/Library/Frameworks/OpenGL.framework/OpenGL") + defer C.free(unsafe.Pointer(clib)) + lib, err := C.dlopen(clib, C.RTLD_NOW|C.RTLD_LOCAL) + if err != nil { + return nil, err + } + csym := C.CString("glFlush") + defer C.free(unsafe.Pointer(csym)) + glFlush := C.PFN_glFlush(C.dlsym(lib, csym)) + if glFlush == nil { + return nil, errors.New("gl: missing symbol glFlush in the OpenGL framework") + } + view := w.contextView() + ctx := C.gio_createGLContext() + if ctx == 0 { + return nil, errors.New("gl: failed to create NSOpenGLContext") + } + C.gio_setContextView(ctx, view) + c := &glContext{ + ctx: ctx, + view: view, + glFlush: glFlush, + } + return c, nil +} + +func (c *glContext) RenderTarget() (gpu.RenderTarget, error) { + return gpu.OpenGLRenderTarget{}, nil +} + +func (c *glContext) API() gpu.API { + return gpu.OpenGL{} +} + +func (c *glContext) Release() { + if c.ctx != 0 { + C.gio_clearCurrentContext() + C.CFRelease(c.ctx) + c.ctx = 0 + } +} + +func (c *glContext) Present() error { + // Assume the caller already locked the context. + C.glFlush(c.glFlush) + return nil +} + +func (c *glContext) Lock() error { + // OpenGL contexts are implicit and thread-local. Lock the OS thread. + runtime.LockOSThread() + + C.gio_lockContext(c.ctx) + C.gio_makeCurrentContext(c.ctx) + return nil +} + +func (c *glContext) Unlock() { + C.gio_clearCurrentContext() + C.gio_unlockContext(c.ctx) +} + +func (c *glContext) Refresh() error { + c.Lock() + defer c.Unlock() + C.gio_updateContext(c.ctx) + return nil +} + +func (w *window) NewContext() (context, error) { + return newContext(w) +} diff --git a/vendor/gioui.org/app/gl_macos.m b/vendor/gioui.org/app/gl_macos.m new file mode 100644 index 0000000..fe83743 --- /dev/null +++ b/vendor/gioui.org/app/gl_macos.m @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +// +build darwin,!ios,nometal + +@import AppKit; + +#include <CoreFoundation/CoreFoundation.h> +#include <OpenGL/OpenGL.h> +#include "_cgo_export.h" + +CALayer *gio_layerFactory(void) { + @autoreleasepool { + return [CALayer layer]; + } +} + +CFTypeRef gio_createGLContext(void) { + @autoreleasepool { + NSOpenGLPixelFormatAttribute attr[] = { + NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, + NSOpenGLPFAColorSize, 24, + NSOpenGLPFAAccelerated, + // Opt-in to automatic GPU switching. CGL-only property. + kCGLPFASupportsAutomaticGraphicsSwitching, + NSOpenGLPFAAllowOfflineRenderers, + 0 + }; + NSOpenGLPixelFormat *pixFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr]; + + NSOpenGLContext *ctx = [[NSOpenGLContext alloc] initWithFormat:pixFormat shareContext: nil]; + return CFBridgingRetain(ctx); + } +} + +void gio_setContextView(CFTypeRef ctxRef, CFTypeRef viewRef) { + NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef; + NSView *view = (__bridge NSView *)viewRef; + [view setWantsBestResolutionOpenGLSurface:YES]; + [ctx setView:view]; +} + +void gio_clearCurrentContext(void) { + @autoreleasepool { + [NSOpenGLContext clearCurrentContext]; + } +} + +void gio_updateContext(CFTypeRef ctxRef) { + @autoreleasepool { + NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef; + [ctx update]; + } +} + +void gio_makeCurrentContext(CFTypeRef ctxRef) { + @autoreleasepool { + NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef; + [ctx makeCurrentContext]; + } +} + +void gio_lockContext(CFTypeRef ctxRef) { + @autoreleasepool { + NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef; + CGLLockContext([ctx CGLContextObj]); + } +} + +void gio_unlockContext(CFTypeRef ctxRef) { + @autoreleasepool { + NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef; + CGLUnlockContext([ctx CGLContextObj]); + } +} diff --git a/vendor/gioui.org/app/internal/d3d11/backend_windows.go b/vendor/gioui.org/app/internal/d3d11/backend_windows.go deleted file mode 100644 index 48ff175..0000000 --- a/vendor/gioui.org/app/internal/d3d11/backend_windows.go +++ /dev/null @@ -1,856 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package d3d11 - -import ( - "errors" - "fmt" - "image" - "math" - "unsafe" - - "gioui.org/gpu/backend" - gunsafe "gioui.org/internal/unsafe" - "golang.org/x/sys/windows" -) - -const debug = false - -type Device struct { - dev *_ID3D11Device - ctx *_ID3D11DeviceContext - featLvl uint32 - floatFormat uint32 - depthStates map[depthState]*_ID3D11DepthStencilState - blendStates map[blendState]*_ID3D11BlendState -} - -type Backend struct { - // Temporary storage to avoid garbage. - clearColor [4]float32 - viewport _D3D11_VIEWPORT - depthState depthState - blendState blendState - prog *Program - - dev *Device - caps backend.Caps - - // fbo is the currently bound fbo. - fbo *Framebuffer -} - -type blendState struct { - enable bool - sfactor backend.BlendFactor - dfactor backend.BlendFactor -} - -type depthState struct { - enable bool - mask bool - fn backend.DepthFunc -} - -type Texture struct { - backend *Backend - format uint32 - bindings backend.BufferBinding - tex *_ID3D11Texture2D - sampler *_ID3D11SamplerState - resView *_ID3D11ShaderResourceView - width int - height int -} - -type Program struct { - backend *Backend - - vert struct { - shader *_ID3D11VertexShader - uniforms *Buffer - } - frag struct { - shader *_ID3D11PixelShader - uniforms *Buffer - } -} - -type Framebuffer struct { - dev *Device - format uint32 - resource *_ID3D11Resource - renderTarget *_ID3D11RenderTargetView - depthView *_ID3D11DepthStencilView - foreign bool -} - -type Buffer struct { - backend *Backend - bind uint32 - buf *_ID3D11Buffer - immutable bool -} - -type InputLayout struct { - dev *Device - layout *_ID3D11InputLayout -} - -type SwapChain struct { - swchain *_IDXGISwapChain - fbo *Framebuffer -} - -func NewDevice() (*Device, error) { - var flags uint32 - if debug { - flags |= _D3D11_CREATE_DEVICE_DEBUG - } - d3ddev, d3dctx, featLvl, err := _D3D11CreateDevice( - _D3D_DRIVER_TYPE_HARDWARE, - flags, - ) - if err != nil { - return nil, fmt.Errorf("NewContext: %v", err) - } - dev := &Device{dev: d3ddev, ctx: d3dctx, featLvl: featLvl} - if featLvl < _D3D_FEATURE_LEVEL_9_1 { - _IUnknownRelease(unsafe.Pointer(d3ddev), d3ddev.vtbl.Release) - _IUnknownRelease(unsafe.Pointer(d3dctx), d3dctx.vtbl.Release) - return nil, fmt.Errorf("d3d11: feature level too low: %d", featLvl) - } - floatFormat, ok := detectFloatFormat(d3ddev) - if !ok { - _IUnknownRelease(unsafe.Pointer(d3ddev), d3ddev.vtbl.Release) - _IUnknownRelease(unsafe.Pointer(d3dctx), d3dctx.vtbl.Release) - return nil, fmt.Errorf("d3d11: no available floating point formats") - } - dev.floatFormat = floatFormat - dev.depthStates = make(map[depthState]*_ID3D11DepthStencilState) - dev.blendStates = make(map[blendState]*_ID3D11BlendState) - return dev, nil -} - -func detectFloatFormat(dev *_ID3D11Device) (uint32, bool) { - formats := []uint32{ - _DXGI_FORMAT_R16_FLOAT, - _DXGI_FORMAT_R32_FLOAT, - _DXGI_FORMAT_R16G16_FLOAT, - _DXGI_FORMAT_R32G32_FLOAT, - // These last two are really wasteful, but c'est la vie. - _DXGI_FORMAT_R16G16B16A16_FLOAT, - _DXGI_FORMAT_R32G32B32A32_FLOAT, - } - for _, format := range formats { - need := uint32(_D3D11_FORMAT_SUPPORT_TEXTURE2D | _D3D11_FORMAT_SUPPORT_RENDER_TARGET) - if support, _ := dev.CheckFormatSupport(format); support&need == need { - return format, true - } - } - return 0, false -} - -func (d *Device) CreateSwapChain(hwnd windows.Handle) (*SwapChain, error) { - dxgiDev, err := _IUnknownQueryInterface(unsafe.Pointer(d.dev), d.dev.vtbl.QueryInterface, &_IID_IDXGIDevice) - if err != nil { - return nil, fmt.Errorf("NewContext: %v", err) - } - adapter, err := (*_IDXGIDevice)(unsafe.Pointer(dxgiDev)).GetAdapter() - _IUnknownRelease(unsafe.Pointer(dxgiDev), dxgiDev.vtbl.Release) - if err != nil { - return nil, fmt.Errorf("NewContext: %v", err) - } - dxgiFactory, err := (*_IDXGIObject)(unsafe.Pointer(adapter)).GetParent(&_IID_IDXGIFactory) - _IUnknownRelease(unsafe.Pointer(adapter), adapter.vtbl.Release) - if err != nil { - return nil, fmt.Errorf("NewContext: %v", err) - } - d3dswchain, err := (*_IDXGIFactory)(unsafe.Pointer(dxgiFactory)).CreateSwapChain( - (*_IUnknown)(unsafe.Pointer(d.dev)), - &_DXGI_SWAP_CHAIN_DESC{ - BufferDesc: _DXGI_MODE_DESC{ - Format: _DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, - }, - SampleDesc: _DXGI_SAMPLE_DESC{ - Count: 1, - }, - BufferUsage: _DXGI_USAGE_RENDER_TARGET_OUTPUT, - BufferCount: 1, - OutputWindow: hwnd, - Windowed: 1, - SwapEffect: _DXGI_SWAP_EFFECT_DISCARD, - }, - ) - _IUnknownRelease(unsafe.Pointer(dxgiFactory), dxgiFactory.vtbl.Release) - if err != nil { - return nil, fmt.Errorf("NewContext: %v", err) - } - return &SwapChain{swchain: d3dswchain, fbo: &Framebuffer{}}, nil -} - -func (s *SwapChain) Framebuffer(d *Device) (*Framebuffer, error) { - if s.fbo.renderTarget != nil { - return s.fbo, nil - } - desc, err := s.swchain.GetDesc() - if err != nil { - return nil, err - } - backBuffer, err := s.swchain.GetBuffer(0, &_IID_ID3D11Texture2D) - if err != nil { - return nil, err - } - texture := (*_ID3D11Resource)(unsafe.Pointer(backBuffer)) - renderTarget, err := d.dev.CreateRenderTargetView(texture) - _IUnknownRelease(unsafe.Pointer(backBuffer), backBuffer.vtbl.Release) - if err != nil { - return nil, err - } - depthView, err := createDepthView(d.dev, int(desc.BufferDesc.Width), int(desc.BufferDesc.Height), 24) - if err != nil { - _IUnknownRelease(unsafe.Pointer(renderTarget), renderTarget.vtbl.Release) - return nil, err - } - s.fbo.renderTarget = renderTarget - s.fbo.depthView = depthView - s.fbo.dev = d - return s.fbo, nil -} - -func (d *Device) Release() { - _IUnknownRelease(unsafe.Pointer(d.ctx), d.ctx.vtbl.Release) - _IUnknownRelease(unsafe.Pointer(d.dev), d.dev.vtbl.Release) - d.ctx = nil - d.dev = nil - for _, state := range d.depthStates { - _IUnknownRelease(unsafe.Pointer(state), state.vtbl.Release) - } - d.depthStates = nil - for _, state := range d.blendStates { - _IUnknownRelease(unsafe.Pointer(state), state.vtbl.Release) - } - d.blendStates = nil -} - -func (s *SwapChain) Resize() error { - if s.fbo.renderTarget != nil { - s.fbo.Release() - } - return s.swchain.ResizeBuffers(0, 0, 0, _DXGI_FORMAT_UNKNOWN, 0) -} - -func (s *SwapChain) Release() { - _IUnknownRelease(unsafe.Pointer(s.swchain), s.swchain.vtbl.Release) -} - -func (s *SwapChain) Present() error { - return s.swchain.Present(1, 0) -} - -func NewBackend(d *Device) (*Backend, error) { - caps := backend.Caps{ - MaxTextureSize: 2048, // 9.1 maximum - } - switch { - case d.featLvl >= _D3D_FEATURE_LEVEL_11_0: - caps.MaxTextureSize = 16384 - case d.featLvl >= _D3D_FEATURE_LEVEL_9_3: - caps.MaxTextureSize = 4096 - } - b := &Backend{dev: d, caps: caps} - // Disable backface culling to match OpenGL. - state, err := b.dev.dev.CreateRasterizerState(&_D3D11_RASTERIZER_DESC{ - CullMode: _D3D11_CULL_NONE, - FillMode: _D3D11_FILL_SOLID, - DepthClipEnable: 1, - }) - // Enable depth mask to match OpenGL. - b.depthState.mask = true - if err != nil { - return nil, err - } - b.dev.ctx.RSSetState(state) - _IUnknownRelease(unsafe.Pointer(state), state.vtbl.Release) - return b, nil -} - -func (b *Backend) BeginFrame() { -} - -func (b *Backend) EndFrame() { -} - -func (b *Backend) Caps() backend.Caps { - return b.caps -} - -func (b *Backend) NewTimer() backend.Timer { - panic("timers not supported") -} - -func (b *Backend) IsTimeContinuous() bool { - panic("timers not supported") -} - -func (b *Backend) NewTexture(format backend.TextureFormat, width, height int, minFilter, magFilter backend.TextureFilter, bindings backend.BufferBinding) (backend.Texture, error) { - var d3dfmt uint32 - switch format { - case backend.TextureFormatFloat: - d3dfmt = b.dev.floatFormat - case backend.TextureFormatSRGB: - d3dfmt = _DXGI_FORMAT_R8G8B8A8_UNORM_SRGB - default: - return nil, fmt.Errorf("unsupported texture format %d", format) - } - tex, err := b.dev.dev.CreateTexture2D(&_D3D11_TEXTURE2D_DESC{ - Width: uint32(width), - Height: uint32(height), - MipLevels: 1, - ArraySize: 1, - Format: d3dfmt, - SampleDesc: _DXGI_SAMPLE_DESC{ - Count: 1, - Quality: 0, - }, - BindFlags: convBufferBinding(bindings), - }) - if err != nil { - return nil, err - } - var ( - sampler *_ID3D11SamplerState - resView *_ID3D11ShaderResourceView - ) - if bindings&backend.BufferBindingTexture != 0 { - var filter uint32 - switch { - case minFilter == backend.FilterNearest && magFilter == backend.FilterNearest: - filter = _D3D11_FILTER_MIN_MAG_MIP_POINT - case minFilter == backend.FilterLinear && magFilter == backend.FilterLinear: - filter = _D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT - default: - _IUnknownRelease(unsafe.Pointer(tex), tex.vtbl.Release) - return nil, fmt.Errorf("unsupported texture filter combination %d, %d", minFilter, magFilter) - } - var err error - sampler, err = b.dev.dev.CreateSamplerState(&_D3D11_SAMPLER_DESC{ - Filter: filter, - AddressU: _D3D11_TEXTURE_ADDRESS_CLAMP, - AddressV: _D3D11_TEXTURE_ADDRESS_CLAMP, - AddressW: _D3D11_TEXTURE_ADDRESS_CLAMP, - MaxAnisotropy: 1, - MinLOD: -math.MaxFloat32, - MaxLOD: math.MaxFloat32, - }) - if err != nil { - _IUnknownRelease(unsafe.Pointer(tex), tex.vtbl.Release) - return nil, err - } - resView, err = b.dev.dev.CreateShaderResourceViewTEX2D( - (*_ID3D11Resource)(unsafe.Pointer(tex)), - &_D3D11_SHADER_RESOURCE_VIEW_DESC_TEX2D{ - _D3D11_SHADER_RESOURCE_VIEW_DESC: _D3D11_SHADER_RESOURCE_VIEW_DESC{ - Format: d3dfmt, - ViewDimension: _D3D11_SRV_DIMENSION_TEXTURE2D, - }, - Texture2D: _D3D11_TEX2D_SRV{ - MostDetailedMip: 0, - MipLevels: ^uint32(0), - }, - }, - ) - if err != nil { - _IUnknownRelease(unsafe.Pointer(tex), tex.vtbl.Release) - _IUnknownRelease(unsafe.Pointer(sampler), sampler.vtbl.Release) - return nil, err - } - } - return &Texture{backend: b, format: d3dfmt, tex: tex, sampler: sampler, resView: resView, bindings: bindings, width: width, height: height}, nil -} - -func (b *Backend) CurrentFramebuffer() backend.Framebuffer { - renderTarget := b.dev.ctx.OMGetRenderTargets() - if renderTarget != nil { - // Assume someone else is holding on to it. - _IUnknownRelease(unsafe.Pointer(renderTarget), renderTarget.vtbl.Release) - } - if renderTarget == b.fbo.renderTarget { - return b.fbo - } - return &Framebuffer{dev: b.dev, renderTarget: renderTarget, foreign: true} -} - -func (b *Backend) NewFramebuffer(tex backend.Texture, depthBits int) (backend.Framebuffer, error) { - d3dtex := tex.(*Texture) - if d3dtex.bindings&backend.BufferBindingFramebuffer == 0 { - return nil, errors.New("the texture was created without BufferBindingFramebuffer binding") - } - resource := (*_ID3D11Resource)(unsafe.Pointer(d3dtex.tex)) - renderTarget, err := b.dev.dev.CreateRenderTargetView(resource) - if err != nil { - return nil, err - } - fbo := &Framebuffer{dev: b.dev, format: d3dtex.format, resource: resource, renderTarget: renderTarget} - if depthBits > 0 { - depthView, err := createDepthView(b.dev.dev, d3dtex.width, d3dtex.height, depthBits) - if err != nil { - _IUnknownRelease(unsafe.Pointer(renderTarget), renderTarget.vtbl.Release) - return nil, err - } - fbo.depthView = depthView - } - return fbo, nil -} - -func createDepthView(d *_ID3D11Device, width, height, depthBits int) (*_ID3D11DepthStencilView, error) { - depthTex, err := d.CreateTexture2D(&_D3D11_TEXTURE2D_DESC{ - Width: uint32(width), - Height: uint32(height), - MipLevels: 1, - ArraySize: 1, - Format: _DXGI_FORMAT_D24_UNORM_S8_UINT, - SampleDesc: _DXGI_SAMPLE_DESC{ - Count: 1, - Quality: 0, - }, - BindFlags: _D3D11_BIND_DEPTH_STENCIL, - }) - if err != nil { - return nil, err - } - depthView, err := d.CreateDepthStencilViewTEX2D( - (*_ID3D11Resource)(unsafe.Pointer(depthTex)), - &_D3D11_DEPTH_STENCIL_VIEW_DESC_TEX2D{ - Format: _DXGI_FORMAT_D24_UNORM_S8_UINT, - ViewDimension: _D3D11_DSV_DIMENSION_TEXTURE2D, - }, - ) - _IUnknownRelease(unsafe.Pointer(depthTex), depthTex.vtbl.Release) - return depthView, err -} - -func (b *Backend) NewInputLayout(vertexShader backend.ShaderSources, layout []backend.InputDesc) (backend.InputLayout, error) { - if len(vertexShader.Inputs) != len(layout) { - return nil, fmt.Errorf("NewInputLayout: got %d inputs, expected %d", len(layout), len(vertexShader.Inputs)) - } - descs := make([]_D3D11_INPUT_ELEMENT_DESC, len(layout)) - for i, l := range layout { - inp := vertexShader.Inputs[i] - cname, err := windows.BytePtrFromString(inp.Semantic) - if err != nil { - return nil, err - } - var format uint32 - switch l.Type { - case backend.DataTypeFloat: - switch l.Size { - case 1: - format = _DXGI_FORMAT_R32_FLOAT - case 2: - format = _DXGI_FORMAT_R32G32_FLOAT - case 3: - format = _DXGI_FORMAT_R32G32B32_FLOAT - case 4: - format = _DXGI_FORMAT_R32G32B32A32_FLOAT - default: - panic("unsupported float data size") - } - case backend.DataTypeShort: - switch l.Size { - case 1: - format = _DXGI_FORMAT_R16_SINT - case 2: - format = _DXGI_FORMAT_R16G16_SINT - default: - panic("unsupported float data size") - } - default: - panic("unsupported data type") - } - descs[i] = _D3D11_INPUT_ELEMENT_DESC{ - SemanticName: cname, - SemanticIndex: uint32(inp.SemanticIndex), - Format: format, - AlignedByteOffset: uint32(l.Offset), - } - } - l, err := b.dev.dev.CreateInputLayout(descs, vertexShader.HLSL) - if err != nil { - return nil, err - } - return &InputLayout{dev: b.dev, layout: l}, nil -} - -func (b *Backend) NewBuffer(typ backend.BufferBinding, size int) (backend.Buffer, error) { - if typ&backend.BufferBindingUniforms != 0 { - if typ != backend.BufferBindingUniforms { - return nil, errors.New("uniform buffers cannot have other bindings") - } - if size%16 != 0 { - return nil, fmt.Errorf("constant buffer size is %d, expected a multiple of 16", size) - } - } - bind := convBufferBinding(typ) - buf, err := b.dev.dev.CreateBuffer(&_D3D11_BUFFER_DESC{ - ByteWidth: uint32(size), - BindFlags: bind, - }, nil) - if err != nil { - return nil, err - } - return &Buffer{backend: b, buf: buf, bind: bind}, nil -} - -func (b *Backend) NewImmutableBuffer(typ backend.BufferBinding, data []byte) (backend.Buffer, error) { - if typ&backend.BufferBindingUniforms != 0 { - if typ != backend.BufferBindingUniforms { - return nil, errors.New("uniform buffers cannot have other bindings") - } - if len(data)%16 != 0 { - return nil, fmt.Errorf("constant buffer size is %d, expected a multiple of 16", len(data)) - } - } - bind := convBufferBinding(typ) - buf, err := b.dev.dev.CreateBuffer(&_D3D11_BUFFER_DESC{ - ByteWidth: uint32(len(data)), - Usage: _D3D11_USAGE_IMMUTABLE, - BindFlags: bind, - }, data) - if err != nil { - return nil, err - } - return &Buffer{backend: b, buf: buf, bind: bind, immutable: true}, nil -} - -func (b *Backend) NewProgram(vertexShader, fragmentShader backend.ShaderSources) (backend.Program, error) { - vs, err := b.dev.dev.CreateVertexShader(vertexShader.HLSL) - if err != nil { - return nil, err - } - ps, err := b.dev.dev.CreatePixelShader(fragmentShader.HLSL) - if err != nil { - return nil, err - } - p := &Program{backend: b} - p.vert.shader = vs - p.frag.shader = ps - return p, nil -} - -func (b *Backend) Clear(colr, colg, colb, cola float32) { - b.clearColor = [4]float32{colr, colg, colb, cola} - b.dev.ctx.ClearRenderTargetView(b.fbo.renderTarget, &b.clearColor) -} - -func (b *Backend) ClearDepth(depth float32) { - if b.fbo.depthView != nil { - b.dev.ctx.ClearDepthStencilView(b.fbo.depthView, _D3D11_CLEAR_DEPTH|_D3D11_CLEAR_STENCIL, depth, 0) - } -} - -func (b *Backend) Viewport(x, y, width, height int) { - b.viewport = _D3D11_VIEWPORT{ - TopLeftX: float32(x), - TopLeftY: float32(y), - Width: float32(width), - Height: float32(height), - MinDepth: 0.0, - MaxDepth: 1.0, - } - b.dev.ctx.RSSetViewports(&b.viewport) -} - -func (b *Backend) DrawArrays(mode backend.DrawMode, off, count int) { - b.prepareDraw(mode) - b.dev.ctx.Draw(uint32(count), uint32(off)) -} - -func (b *Backend) DrawElements(mode backend.DrawMode, off, count int) { - b.prepareDraw(mode) - b.dev.ctx.DrawIndexed(uint32(count), uint32(off), 0) -} - -func (b *Backend) prepareDraw(mode backend.DrawMode) { - if p := b.prog; p != nil { - b.dev.ctx.VSSetShader(p.vert.shader) - b.dev.ctx.PSSetShader(p.frag.shader) - if buf := p.vert.uniforms; buf != nil { - b.dev.ctx.VSSetConstantBuffers(buf.buf) - } - if buf := p.frag.uniforms; buf != nil { - b.dev.ctx.PSSetConstantBuffers(buf.buf) - } - } - var topology uint32 - switch mode { - case backend.DrawModeTriangles: - topology = _D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST - case backend.DrawModeTriangleStrip: - topology = _D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP - default: - panic("unsupported draw mode") - } - b.dev.ctx.IASetPrimitiveTopology(topology) - - depthState, ok := b.dev.depthStates[b.depthState] - if !ok { - var desc _D3D11_DEPTH_STENCIL_DESC - if b.depthState.enable { - desc.DepthEnable = 1 - } - if b.depthState.mask { - desc.DepthWriteMask = _D3D11_DEPTH_WRITE_MASK_ALL - } - switch b.depthState.fn { - case backend.DepthFuncGreater: - desc.DepthFunc = _D3D11_COMPARISON_GREATER - case backend.DepthFuncGreaterEqual: - desc.DepthFunc = _D3D11_COMPARISON_GREATER_EQUAL - default: - panic("unsupported depth func") - } - var err error - depthState, err = b.dev.dev.CreateDepthStencilState(&desc) - if err != nil { - panic(err) - } - b.dev.depthStates[b.depthState] = depthState - } - b.dev.ctx.OMSetDepthStencilState(depthState, 0) - - blendState, ok := b.dev.blendStates[b.blendState] - if !ok { - var desc _D3D11_BLEND_DESC - t0 := &desc.RenderTarget[0] - t0.RenderTargetWriteMask = _D3D11_COLOR_WRITE_ENABLE_ALL - t0.BlendOp = _D3D11_BLEND_OP_ADD - t0.BlendOpAlpha = _D3D11_BLEND_OP_ADD - if b.blendState.enable { - t0.BlendEnable = 1 - } - scol, salpha := toBlendFactor(b.blendState.sfactor) - dcol, dalpha := toBlendFactor(b.blendState.dfactor) - t0.SrcBlend = scol - t0.SrcBlendAlpha = salpha - t0.DestBlend = dcol - t0.DestBlendAlpha = dalpha - var err error - blendState, err = b.dev.dev.CreateBlendState(&desc) - if err != nil { - panic(err) - } - b.dev.blendStates[b.blendState] = blendState - } - b.dev.ctx.OMSetBlendState(blendState, nil, 0xffffffff) -} - -func (b *Backend) DepthFunc(f backend.DepthFunc) { - b.depthState.fn = f -} - -func (b *Backend) SetBlend(enable bool) { - b.blendState.enable = enable -} - -func (b *Backend) SetDepthTest(enable bool) { - b.depthState.enable = enable -} - -func (b *Backend) DepthMask(mask bool) { - b.depthState.mask = mask -} - -func (b *Backend) BlendFunc(sfactor, dfactor backend.BlendFactor) { - b.blendState.sfactor = sfactor - b.blendState.dfactor = dfactor -} - -func (t *Texture) Upload(img *image.RGBA) { - b := img.Bounds() - w := b.Dx() - if img.Stride != w*4 { - panic("unsupported stride") - } - start := (b.Min.X + b.Min.Y*w) * 4 - end := (b.Max.X + (b.Max.Y-1)*w) * 4 - pixels := img.Pix[start:end] - res := (*_ID3D11Resource)(unsafe.Pointer(t.tex)) - t.backend.dev.ctx.UpdateSubresource(res, uint32(img.Stride), uint32(len(pixels)), pixels) -} - -func (t *Texture) Release() { - _IUnknownRelease(unsafe.Pointer(t.tex), t.tex.vtbl.Release) - t.tex = nil - if t.sampler != nil { - _IUnknownRelease(unsafe.Pointer(t.sampler), t.sampler.vtbl.Release) - t.sampler = nil - } - if t.resView != nil { - _IUnknownRelease(unsafe.Pointer(t.resView), t.resView.vtbl.Release) - t.resView = nil - } -} - -func (b *Backend) BindTexture(unit int, tex backend.Texture) { - t := tex.(*Texture) - b.dev.ctx.PSSetSamplers(uint32(unit), t.sampler) - b.dev.ctx.PSSetShaderResources(uint32(unit), t.resView) -} - -func (b *Backend) BindProgram(prog backend.Program) { - b.prog = prog.(*Program) -} - -func (p *Program) Release() { - _IUnknownRelease(unsafe.Pointer(p.vert.shader), p.vert.shader.vtbl.Release) - _IUnknownRelease(unsafe.Pointer(p.frag.shader), p.frag.shader.vtbl.Release) - p.vert.shader = nil - p.frag.shader = nil -} - -func (p *Program) SetVertexUniforms(buf backend.Buffer) { - p.vert.uniforms = buf.(*Buffer) -} - -func (p *Program) SetFragmentUniforms(buf backend.Buffer) { - p.frag.uniforms = buf.(*Buffer) -} - -func (b *Backend) BindVertexBuffer(buf backend.Buffer, stride, offset int) { - b.dev.ctx.IASetVertexBuffers(buf.(*Buffer).buf, uint32(stride), uint32(offset)) -} - -func (b *Backend) BindIndexBuffer(buf backend.Buffer) { - b.dev.ctx.IASetIndexBuffer(buf.(*Buffer).buf, _DXGI_FORMAT_R16_UINT, 0) -} - -func (b *Buffer) Upload(data []byte) { - b.backend.dev.ctx.UpdateSubresource((*_ID3D11Resource)(unsafe.Pointer(b.buf)), 0, 0, data) -} - -func (b *Buffer) Release() { - _IUnknownRelease(unsafe.Pointer(b.buf), b.buf.vtbl.Release) - b.buf = nil -} - -func (f *Framebuffer) ReadPixels(src image.Rectangle, pixels []byte) error { - if f.resource == nil { - return errors.New("framebuffer does not support ReadPixels") - } - w, h := src.Dx(), src.Dy() - tex, err := f.dev.dev.CreateTexture2D(&_D3D11_TEXTURE2D_DESC{ - Width: uint32(w), - Height: uint32(h), - MipLevels: 1, - ArraySize: 1, - Format: f.format, - SampleDesc: _DXGI_SAMPLE_DESC{ - Count: 1, - Quality: 0, - }, - Usage: _D3D11_USAGE_STAGING, - CPUAccessFlags: _D3D11_CPU_ACCESS_READ, - }) - if err != nil { - return fmt.Errorf("ReadPixels: %v", err) - } - defer _IUnknownRelease(unsafe.Pointer(tex), tex.vtbl.Release) - res := (*_ID3D11Resource)(unsafe.Pointer(tex)) - f.dev.ctx.CopySubresourceRegion( - res, - 0, // Destination subresource. - 0, 0, 0, // Destination coordinates (x, y, z). - f.resource, - 0, // Source subresource. - &_D3D11_BOX{ - left: uint32(src.Min.X), - top: uint32(src.Min.Y), - right: uint32(src.Max.X), - bottom: uint32(src.Max.Y), - front: 0, - back: 1, - }, - ) - resMap, err := f.dev.ctx.Map(res, 0, _D3D11_MAP_READ, 0) - if err != nil { - return fmt.Errorf("ReadPixels: %v", err) - } - defer f.dev.ctx.Unmap(res, 0) - srcPitch := w * 4 - dstPitch := int(resMap.RowPitch) - mapSize := dstPitch * h - data := gunsafe.SliceOf(resMap.pData)[:mapSize:mapSize] - width := w * 4 - for r := 0; r < h; r++ { - pixels := pixels[r*srcPitch:] - copy(pixels[:width], data[r*dstPitch:]) - } - return nil -} - -func (b *Backend) BindFramebuffer(fbo backend.Framebuffer) { - b.fbo = fbo.(*Framebuffer) - b.dev.ctx.OMSetRenderTargets(b.fbo.renderTarget, b.fbo.depthView) -} - -func (f *Framebuffer) Invalidate() { -} - -func (f *Framebuffer) Release() { - if f.foreign { - panic("cannot release Framebuffer from CurrentFramebuffer") - } - if f.renderTarget != nil { - _IUnknownRelease(unsafe.Pointer(f.renderTarget), f.renderTarget.vtbl.Release) - f.renderTarget = nil - } - if f.depthView != nil { - _IUnknownRelease(unsafe.Pointer(f.depthView), f.depthView.vtbl.Release) - f.depthView = nil - } -} - -func (b *Backend) BindInputLayout(layout backend.InputLayout) { - b.dev.ctx.IASetInputLayout(layout.(*InputLayout).layout) -} - -func (l *InputLayout) Release() { - _IUnknownRelease(unsafe.Pointer(l.layout), l.layout.vtbl.Release) - l.layout = nil -} - -func convBufferBinding(typ backend.BufferBinding) uint32 { - var bindings uint32 - if typ&backend.BufferBindingVertices != 0 { - bindings |= _D3D11_BIND_VERTEX_BUFFER - } - if typ&backend.BufferBindingIndices != 0 { - bindings |= _D3D11_BIND_INDEX_BUFFER - } - if typ&backend.BufferBindingUniforms != 0 { - bindings |= _D3D11_BIND_CONSTANT_BUFFER - } - if typ&backend.BufferBindingTexture != 0 { - bindings |= _D3D11_BIND_SHADER_RESOURCE - } - if typ&backend.BufferBindingFramebuffer != 0 { - bindings |= _D3D11_BIND_RENDER_TARGET - } - return bindings -} - -func toBlendFactor(f backend.BlendFactor) (uint32, uint32) { - switch f { - case backend.BlendFactorOne: - return _D3D11_BLEND_ONE, _D3D11_BLEND_ONE - case backend.BlendFactorOneMinusSrcAlpha: - return _D3D11_BLEND_INV_SRC_ALPHA, _D3D11_BLEND_INV_SRC_ALPHA - case backend.BlendFactorZero: - return _D3D11_BLEND_ZERO, _D3D11_BLEND_ZERO - case backend.BlendFactorDstColor: - return _D3D11_BLEND_DEST_COLOR, _D3D11_BLEND_DEST_ALPHA - default: - panic("unsupported blend source factor") - } -} diff --git a/vendor/gioui.org/app/internal/glimpl/gl.go b/vendor/gioui.org/app/internal/glimpl/gl.go deleted file mode 100644 index 5b23e4e..0000000 --- a/vendor/gioui.org/app/internal/glimpl/gl.go +++ /dev/null @@ -1,541 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -// +build darwin linux freebsd openbsd - -package glimpl - -import ( - "runtime" - "strings" - "unsafe" - - "gioui.org/gpu/gl" -) - -/* -#cgo CFLAGS: -Werror -#cgo linux,!android pkg-config: glesv2 -#cgo linux freebsd LDFLAGS: -ldl -#cgo freebsd openbsd android LDFLAGS: -lGLESv2 -#cgo freebsd CFLAGS: -I/usr/local/include -#cgo freebsd LDFLAGS: -L/usr/local/lib -#cgo openbsd CFLAGS: -I/usr/X11R6/include -#cgo openbsd LDFLAGS: -L/usr/X11R6/lib -#cgo darwin,!ios CFLAGS: -DGL_SILENCE_DEPRECATION -#cgo darwin,!ios LDFLAGS: -framework OpenGL -#cgo darwin,ios CFLAGS: -DGLES_SILENCE_DEPRECATION -#cgo darwin,ios LDFLAGS: -framework OpenGLES - -#include <stdlib.h> - -#ifdef __APPLE__ - #include "TargetConditionals.h" - #if TARGET_OS_IPHONE - #include <OpenGLES/ES3/gl.h> - #else - #include <OpenGL/gl3.h> - #endif -#else -#define __USE_GNU -#include <dlfcn.h> -#include <GLES2/gl2.h> -#include <GLES3/gl3.h> -#endif - -static void (*_glBindBufferBase)(GLenum target, GLuint index, GLuint buffer); -static GLuint (*_glGetUniformBlockIndex)(GLuint program, const GLchar *uniformBlockName); -static void (*_glUniformBlockBinding)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); -static void (*_glInvalidateFramebuffer)(GLenum target, GLsizei numAttachments, const GLenum *attachments); - -static void (*_glBeginQuery)(GLenum target, GLuint id); -static void (*_glDeleteQueries)(GLsizei n, const GLuint *ids); -static void (*_glEndQuery)(GLenum target); -static void (*_glGenQueries)(GLsizei n, GLuint *ids); -static void (*_glGetQueryObjectuiv)(GLuint id, GLenum pname, GLuint *params); -static const GLubyte* (*_glGetStringi)(GLenum name, GLuint index); - -// The pointer-free version of glVertexAttribPointer, to avoid the Cgo pointer checks. -__attribute__ ((visibility ("hidden"))) void gio_glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, uintptr_t offset) { - glVertexAttribPointer(index, size, type, normalized, stride, (const GLvoid *)offset); -} - -// The pointer-free version of glDrawElements, to avoid the Cgo pointer checks. -__attribute__ ((visibility ("hidden"))) void gio_glDrawElements(GLenum mode, GLsizei count, GLenum type, const uintptr_t offset) { - glDrawElements(mode, count, type, (const GLvoid *)offset); -} - -__attribute__ ((visibility ("hidden"))) void gio_glBindBufferBase(GLenum target, GLuint index, GLuint buffer) { - _glBindBufferBase(target, index, buffer); -} - -__attribute__ ((visibility ("hidden"))) void gio_glUniformBlockBinding(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding) { - _glUniformBlockBinding(program, uniformBlockIndex, uniformBlockBinding); -} - -__attribute__ ((visibility ("hidden"))) GLuint gio_glGetUniformBlockIndex(GLuint program, const GLchar *uniformBlockName) { - return _glGetUniformBlockIndex(program, uniformBlockName); -} - -__attribute__ ((visibility ("hidden"))) void gio_glInvalidateFramebuffer(GLenum target, GLenum attachment) { - // gl.Framebuffer invalidation is just a hint and can safely be ignored. - if (_glInvalidateFramebuffer != NULL) { - _glInvalidateFramebuffer(target, 1, &attachment); - } -} - -__attribute__ ((visibility ("hidden"))) void gio_glBeginQuery(GLenum target, GLenum attachment) { - _glBeginQuery(target, attachment); -} - -__attribute__ ((visibility ("hidden"))) void gio_glDeleteQueries(GLsizei n, const GLuint *ids) { - _glDeleteQueries(n, ids); -} - -__attribute__ ((visibility ("hidden"))) void gio_glEndQuery(GLenum target) { - _glEndQuery(target); -} - -__attribute__ ((visibility ("hidden"))) const GLubyte* gio_glGetStringi(GLenum name, GLuint index) { - if (_glGetStringi == NULL) { - return NULL; - } - return _glGetStringi(name, index); -} - -__attribute__ ((visibility ("hidden"))) void gio_glGenQueries(GLsizei n, GLuint *ids) { - _glGenQueries(n, ids); -} - -__attribute__ ((visibility ("hidden"))) void gio_glGetQueryObjectuiv(GLuint id, GLenum pname, GLuint *params) { - _glGetQueryObjectuiv(id, pname, params); -} - -__attribute__((constructor)) static void gio_loadGLFunctions() { -#ifdef __APPLE__ - #if TARGET_OS_IPHONE - _glInvalidateFramebuffer = glInvalidateFramebuffer; - _glBeginQuery = glBeginQuery; - _glDeleteQueries = glDeleteQueries; - _glEndQuery = glEndQuery; - _glGenQueries = glGenQueries; - _glGetQueryObjectuiv = glGetQueryObjectuiv; - #endif - _glBindBufferBase = glBindBufferBase; - _glGetUniformBlockIndex = glGetUniformBlockIndex; - _glUniformBlockBinding = glUniformBlockBinding; - _glGetStringi = glGetStringi; -#else - // Load libGLESv3 if available. - dlopen("libGLESv3.so", RTLD_NOW | RTLD_GLOBAL); - _glBindBufferBase = dlsym(RTLD_DEFAULT, "glBindBufferBase"); - _glGetUniformBlockIndex = dlsym(RTLD_DEFAULT, "glGetUniformBlockIndex"); - _glUniformBlockBinding = dlsym(RTLD_DEFAULT, "glUniformBlockBinding"); - _glInvalidateFramebuffer = dlsym(RTLD_DEFAULT, "glInvalidateFramebuffer"); - _glGetStringi = dlsym(RTLD_DEFAULT, "glGetStringi"); - // Fall back to EXT_invalidate_framebuffer if available. - if (_glInvalidateFramebuffer == NULL) { - _glInvalidateFramebuffer = dlsym(RTLD_DEFAULT, "glDiscardFramebufferEXT"); - } - - _glBeginQuery = dlsym(RTLD_DEFAULT, "glBeginQuery"); - if (_glBeginQuery == NULL) - _glBeginQuery = dlsym(RTLD_DEFAULT, "glBeginQueryEXT"); - _glDeleteQueries = dlsym(RTLD_DEFAULT, "glDeleteQueries"); - if (_glDeleteQueries == NULL) - _glDeleteQueries = dlsym(RTLD_DEFAULT, "glDeleteQueriesEXT"); - _glEndQuery = dlsym(RTLD_DEFAULT, "glEndQuery"); - if (_glEndQuery == NULL) - _glEndQuery = dlsym(RTLD_DEFAULT, "glEndQueryEXT"); - _glGenQueries = dlsym(RTLD_DEFAULT, "glGenQueries"); - if (_glGenQueries == NULL) - _glGenQueries = dlsym(RTLD_DEFAULT, "glGenQueriesEXT"); - _glGetQueryObjectuiv = dlsym(RTLD_DEFAULT, "glGetQueryObjectuiv"); - if (_glGetQueryObjectuiv == NULL) - _glGetQueryObjectuiv = dlsym(RTLD_DEFAULT, "glGetQueryObjectuivEXT"); -#endif -} -*/ -import "C" - -type Functions struct { - // gl.Query caches. - uints [100]C.GLuint - ints [100]C.GLint -} - -func (f *Functions) ActiveTexture(texture gl.Enum) { - C.glActiveTexture(C.GLenum(texture)) -} - -func (f *Functions) AttachShader(p gl.Program, s gl.Shader) { - C.glAttachShader(C.GLuint(p.V), C.GLuint(s.V)) -} - -func (f *Functions) BeginQuery(target gl.Enum, query gl.Query) { - C.gio_glBeginQuery(C.GLenum(target), C.GLenum(query.V)) -} - -func (f *Functions) BindAttribLocation(p gl.Program, a gl.Attrib, name string) { - cname := C.CString(name) - defer C.free(unsafe.Pointer(cname)) - C.glBindAttribLocation(C.GLuint(p.V), C.GLuint(a), cname) -} - -func (f *Functions) BindBufferBase(target gl.Enum, index int, b gl.Buffer) { - C.gio_glBindBufferBase(C.GLenum(target), C.GLuint(index), C.GLuint(b.V)) -} - -func (f *Functions) BindBuffer(target gl.Enum, b gl.Buffer) { - C.glBindBuffer(C.GLenum(target), C.GLuint(b.V)) -} - -func (f *Functions) BindFramebuffer(target gl.Enum, fb gl.Framebuffer) { - C.glBindFramebuffer(C.GLenum(target), C.GLuint(fb.V)) -} - -func (f *Functions) BindRenderbuffer(target gl.Enum, fb gl.Renderbuffer) { - C.glBindRenderbuffer(C.GLenum(target), C.GLuint(fb.V)) -} - -func (f *Functions) BindTexture(target gl.Enum, t gl.Texture) { - C.glBindTexture(C.GLenum(target), C.GLuint(t.V)) -} - -func (f *Functions) BlendEquation(mode gl.Enum) { - C.glBlendEquation(C.GLenum(mode)) -} - -func (f *Functions) BlendFunc(sfactor, dfactor gl.Enum) { - C.glBlendFunc(C.GLenum(sfactor), C.GLenum(dfactor)) -} - -func (f *Functions) BufferData(target gl.Enum, src []byte, usage gl.Enum) { - var p unsafe.Pointer - if len(src) > 0 { - p = unsafe.Pointer(&src[0]) - } - C.glBufferData(C.GLenum(target), C.GLsizeiptr(len(src)), p, C.GLenum(usage)) -} - -func (f *Functions) CheckFramebufferStatus(target gl.Enum) gl.Enum { - return gl.Enum(C.glCheckFramebufferStatus(C.GLenum(target))) -} - -func (f *Functions) Clear(mask gl.Enum) { - C.glClear(C.GLbitfield(mask)) -} - -func (f *Functions) ClearColor(red float32, green float32, blue float32, alpha float32) { - C.glClearColor(C.GLfloat(red), C.GLfloat(green), C.GLfloat(blue), C.GLfloat(alpha)) -} - -func (f *Functions) ClearDepthf(d float32) { - C.glClearDepthf(C.GLfloat(d)) -} - -func (f *Functions) CompileShader(s gl.Shader) { - C.glCompileShader(C.GLuint(s.V)) -} - -func (f *Functions) CreateBuffer() gl.Buffer { - C.glGenBuffers(1, &f.uints[0]) - return gl.Buffer{uint(f.uints[0])} -} - -func (f *Functions) CreateFramebuffer() gl.Framebuffer { - C.glGenFramebuffers(1, &f.uints[0]) - return gl.Framebuffer{uint(f.uints[0])} -} - -func (f *Functions) CreateProgram() gl.Program { - return gl.Program{uint(C.glCreateProgram())} -} - -func (f *Functions) CreateQuery() gl.Query { - C.gio_glGenQueries(1, &f.uints[0]) - return gl.Query{uint(f.uints[0])} -} - -func (f *Functions) CreateRenderbuffer() gl.Renderbuffer { - C.glGenRenderbuffers(1, &f.uints[0]) - return gl.Renderbuffer{uint(f.uints[0])} -} - -func (f *Functions) CreateShader(ty gl.Enum) gl.Shader { - return gl.Shader{uint(C.glCreateShader(C.GLenum(ty)))} -} - -func (f *Functions) CreateTexture() gl.Texture { - C.glGenTextures(1, &f.uints[0]) - return gl.Texture{uint(f.uints[0])} -} - -func (f *Functions) DeleteBuffer(v gl.Buffer) { - f.uints[0] = C.GLuint(v.V) - C.glDeleteBuffers(1, &f.uints[0]) -} - -func (f *Functions) DeleteFramebuffer(v gl.Framebuffer) { - f.uints[0] = C.GLuint(v.V) - C.glDeleteFramebuffers(1, &f.uints[0]) -} - -func (f *Functions) DeleteProgram(p gl.Program) { - C.glDeleteProgram(C.GLuint(p.V)) -} - -func (f *Functions) DeleteQuery(query gl.Query) { - f.uints[0] = C.GLuint(query.V) - C.gio_glDeleteQueries(1, &f.uints[0]) -} - -func (f *Functions) DeleteRenderbuffer(v gl.Renderbuffer) { - f.uints[0] = C.GLuint(v.V) - C.glDeleteRenderbuffers(1, &f.uints[0]) -} - -func (f *Functions) DeleteShader(s gl.Shader) { - C.glDeleteShader(C.GLuint(s.V)) -} - -func (f *Functions) DeleteTexture(v gl.Texture) { - f.uints[0] = C.GLuint(v.V) - C.glDeleteTextures(1, &f.uints[0]) -} - -func (f *Functions) DepthFunc(v gl.Enum) { - C.glDepthFunc(C.GLenum(v)) -} - -func (f *Functions) DepthMask(mask bool) { - m := C.GLboolean(C.GL_FALSE) - if mask { - m = C.GLboolean(C.GL_TRUE) - } - C.glDepthMask(m) -} - -func (f *Functions) DisableVertexAttribArray(a gl.Attrib) { - C.glDisableVertexAttribArray(C.GLuint(a)) -} - -func (f *Functions) Disable(cap gl.Enum) { - C.glDisable(C.GLenum(cap)) -} - -func (f *Functions) DrawArrays(mode gl.Enum, first int, count int) { - C.glDrawArrays(C.GLenum(mode), C.GLint(first), C.GLsizei(count)) -} - -func (f *Functions) DrawElements(mode gl.Enum, count int, ty gl.Enum, offset int) { - C.gio_glDrawElements(C.GLenum(mode), C.GLsizei(count), C.GLenum(ty), C.uintptr_t(offset)) -} - -func (f *Functions) Enable(cap gl.Enum) { - C.glEnable(C.GLenum(cap)) -} - -func (f *Functions) EndQuery(target gl.Enum) { - C.gio_glEndQuery(C.GLenum(target)) -} - -func (f *Functions) EnableVertexAttribArray(a gl.Attrib) { - C.glEnableVertexAttribArray(C.GLuint(a)) -} - -func (f *Functions) Finish() { - C.glFinish() -} - -func (f *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget gl.Enum, renderbuffer gl.Renderbuffer) { - C.glFramebufferRenderbuffer(C.GLenum(target), C.GLenum(attachment), C.GLenum(renderbuffertarget), C.GLuint(renderbuffer.V)) -} - -func (f *Functions) FramebufferTexture2D(target, attachment, texTarget gl.Enum, t gl.Texture, level int) { - C.glFramebufferTexture2D(C.GLenum(target), C.GLenum(attachment), C.GLenum(texTarget), C.GLuint(t.V), C.GLint(level)) -} - -func (c *Functions) GetBinding(pname gl.Enum) gl.Object { - return gl.Object{uint(c.GetInteger(pname))} -} - -func (f *Functions) GetError() gl.Enum { - return gl.Enum(C.glGetError()) -} - -func (f *Functions) GetRenderbufferParameteri(target, pname gl.Enum) int { - C.glGetRenderbufferParameteriv(C.GLenum(target), C.GLenum(pname), &f.ints[0]) - return int(f.ints[0]) -} - -func (f *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname gl.Enum) int { - C.glGetFramebufferAttachmentParameteriv(C.GLenum(target), C.GLenum(attachment), C.GLenum(pname), &f.ints[0]) - return int(f.ints[0]) -} - -func (f *Functions) GetInteger(pname gl.Enum) int { - C.glGetIntegerv(C.GLenum(pname), &f.ints[0]) - return int(f.ints[0]) -} - -func (f *Functions) GetProgrami(p gl.Program, pname gl.Enum) int { - C.glGetProgramiv(C.GLuint(p.V), C.GLenum(pname), &f.ints[0]) - return int(f.ints[0]) -} - -func (f *Functions) GetProgramInfoLog(p gl.Program) string { - n := f.GetProgrami(p, gl.INFO_LOG_LENGTH) - buf := make([]byte, n) - C.glGetProgramInfoLog(C.GLuint(p.V), C.GLsizei(len(buf)), nil, (*C.GLchar)(unsafe.Pointer(&buf[0]))) - return string(buf) -} - -func (f *Functions) GetQueryObjectuiv(query gl.Query, pname gl.Enum) uint { - C.gio_glGetQueryObjectuiv(C.GLuint(query.V), C.GLenum(pname), &f.uints[0]) - return uint(f.uints[0]) -} - -func (f *Functions) GetShaderi(s gl.Shader, pname gl.Enum) int { - C.glGetShaderiv(C.GLuint(s.V), C.GLenum(pname), &f.ints[0]) - return int(f.ints[0]) -} - -func (f *Functions) GetShaderInfoLog(s gl.Shader) string { - n := f.GetShaderi(s, gl.INFO_LOG_LENGTH) - buf := make([]byte, n) - C.glGetShaderInfoLog(C.GLuint(s.V), C.GLsizei(len(buf)), nil, (*C.GLchar)(unsafe.Pointer(&buf[0]))) - return string(buf) -} - -func (f *Functions) GetStringi(pname gl.Enum, index int) string { - str := C.gio_glGetStringi(C.GLenum(pname), C.GLuint(index)) - if str == nil { - return "" - } - return C.GoString((*C.char)(unsafe.Pointer(str))) -} - -func (f *Functions) GetString(pname gl.Enum) string { - switch { - case runtime.GOOS == "darwin" && pname == gl.EXTENSIONS: - // macOS OpenGL 3 core profile doesn't support glGetString(GL_EXTENSIONS). - // Use glGetStringi(GL_EXTENSIONS, <index>). - var exts []string - nexts := f.GetInteger(gl.NUM_EXTENSIONS) - for i := 0; i < nexts; i++ { - ext := f.GetStringi(gl.EXTENSIONS, i) - exts = append(exts, ext) - } - return strings.Join(exts, " ") - default: - str := C.glGetString(C.GLenum(pname)) - return C.GoString((*C.char)(unsafe.Pointer(str))) - } -} - -func (f *Functions) GetUniformBlockIndex(p gl.Program, name string) uint { - cname := C.CString(name) - defer C.free(unsafe.Pointer(cname)) - return uint(C.gio_glGetUniformBlockIndex(C.GLuint(p.V), cname)) -} - -func (f *Functions) GetUniformLocation(p gl.Program, name string) gl.Uniform { - cname := C.CString(name) - defer C.free(unsafe.Pointer(cname)) - return gl.Uniform{int(C.glGetUniformLocation(C.GLuint(p.V), cname))} -} - -func (f *Functions) InvalidateFramebuffer(target, attachment gl.Enum) { - C.gio_glInvalidateFramebuffer(C.GLenum(target), C.GLenum(attachment)) -} - -func (f *Functions) LinkProgram(p gl.Program) { - C.glLinkProgram(C.GLuint(p.V)) -} - -func (f *Functions) PixelStorei(pname gl.Enum, param int32) { - C.glPixelStorei(C.GLenum(pname), C.GLint(param)) -} - -func (f *Functions) Scissor(x, y, width, height int32) { - C.glScissor(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height)) -} - -func (f *Functions) ReadPixels(x, y, width, height int, format, ty gl.Enum, data []byte) { - var p unsafe.Pointer - if len(data) > 0 { - p = unsafe.Pointer(&data[0]) - } - C.glReadPixels(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height), C.GLenum(format), C.GLenum(ty), p) -} - -func (f *Functions) RenderbufferStorage(target, internalformat gl.Enum, width, height int) { - C.glRenderbufferStorage(C.GLenum(target), C.GLenum(internalformat), C.GLsizei(width), C.GLsizei(height)) -} - -func (f *Functions) ShaderSource(s gl.Shader, src string) { - csrc := C.CString(src) - defer C.free(unsafe.Pointer(csrc)) - strlen := C.GLint(len(src)) - C.glShaderSource(C.GLuint(s.V), 1, &csrc, &strlen) -} - -func (f *Functions) TexImage2D(target gl.Enum, level int, internalFormat int, width int, height int, format gl.Enum, ty gl.Enum, data []byte) { - var p unsafe.Pointer - if len(data) > 0 { - p = unsafe.Pointer(&data[0]) - } - C.glTexImage2D(C.GLenum(target), C.GLint(level), C.GLint(internalFormat), C.GLsizei(width), C.GLsizei(height), 0, C.GLenum(format), C.GLenum(ty), p) -} - -func (f *Functions) TexSubImage2D(target gl.Enum, level int, x int, y int, width int, height int, format gl.Enum, ty gl.Enum, data []byte) { - var p unsafe.Pointer - if len(data) > 0 { - p = unsafe.Pointer(&data[0]) - } - C.glTexSubImage2D(C.GLenum(target), C.GLint(level), C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height), C.GLenum(format), C.GLenum(ty), p) -} - -func (f *Functions) TexParameteri(target, pname gl.Enum, param int) { - C.glTexParameteri(C.GLenum(target), C.GLenum(pname), C.GLint(param)) -} - -func (f *Functions) UniformBlockBinding(p gl.Program, uniformBlockIndex uint, uniformBlockBinding uint) { - C.gio_glUniformBlockBinding(C.GLuint(p.V), C.GLuint(uniformBlockIndex), C.GLuint(uniformBlockBinding)) -} - -func (f *Functions) Uniform1f(dst gl.Uniform, v float32) { - C.glUniform1f(C.GLint(dst.V), C.GLfloat(v)) -} - -func (f *Functions) Uniform1i(dst gl.Uniform, v int) { - C.glUniform1i(C.GLint(dst.V), C.GLint(v)) -} - -func (f *Functions) Uniform2f(dst gl.Uniform, v0 float32, v1 float32) { - C.glUniform2f(C.GLint(dst.V), C.GLfloat(v0), C.GLfloat(v1)) -} - -func (f *Functions) Uniform3f(dst gl.Uniform, v0 float32, v1 float32, v2 float32) { - C.glUniform3f(C.GLint(dst.V), C.GLfloat(v0), C.GLfloat(v1), C.GLfloat(v2)) -} - -func (f *Functions) Uniform4f(dst gl.Uniform, v0 float32, v1 float32, v2 float32, v3 float32) { - C.glUniform4f(C.GLint(dst.V), C.GLfloat(v0), C.GLfloat(v1), C.GLfloat(v2), C.GLfloat(v3)) -} - -func (f *Functions) UseProgram(p gl.Program) { - C.glUseProgram(C.GLuint(p.V)) -} - -func (f *Functions) VertexAttribPointer(dst gl.Attrib, size int, ty gl.Enum, normalized bool, stride int, offset int) { - var n C.GLboolean = C.GL_FALSE - if normalized { - n = C.GL_TRUE - } - C.gio_glVertexAttribPointer(C.GLuint(dst), C.GLint(size), C.GLenum(ty), n, C.GLsizei(stride), C.uintptr_t(offset)) -} - -func (f *Functions) Viewport(x int, y int, width int, height int) { - C.glViewport(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height)) -} diff --git a/vendor/gioui.org/app/internal/glimpl/gl_js.go b/vendor/gioui.org/app/internal/glimpl/gl_js.go deleted file mode 100644 index e4291d0..0000000 --- a/vendor/gioui.org/app/internal/glimpl/gl_js.go +++ /dev/null @@ -1,339 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package glimpl - -import ( - "errors" - "strings" - "syscall/js" - - "gioui.org/gpu/gl" -) - -type Functions struct { - Ctx js.Value - EXT_disjoint_timer_query js.Value - EXT_disjoint_timer_query_webgl2 js.Value - - // Cached JS arrays. - byteBuf js.Value - int32Buf js.Value -} - -func (f *Functions) Init(version int) error { - if version < 2 { - f.EXT_disjoint_timer_query = f.getExtension("EXT_disjoint_timer_query") - if f.getExtension("OES_texture_half_float").IsNull() && f.getExtension("OES_texture_float").IsNull() { - return errors.New("gl: no support for neither OES_texture_half_float nor OES_texture_float") - } - if f.getExtension("EXT_sRGB").IsNull() { - return errors.New("gl: EXT_sRGB not supported") - } - } else { - // WebGL2 extensions. - f.EXT_disjoint_timer_query_webgl2 = f.getExtension("EXT_disjoint_timer_query_webgl2") - if f.getExtension("EXT_color_buffer_half_float").IsNull() && f.getExtension("EXT_color_buffer_float").IsNull() { - return errors.New("gl: no support for neither EXT_color_buffer_half_float nor EXT_color_buffer_float") - } - } - return nil -} - -func (f *Functions) getExtension(name string) js.Value { - return f.Ctx.Call("getExtension", name) -} - -func (f *Functions) ActiveTexture(t gl.Enum) { - f.Ctx.Call("activeTexture", int(t)) -} -func (f *Functions) AttachShader(p gl.Program, s gl.Shader) { - f.Ctx.Call("attachShader", js.Value(p), js.Value(s)) -} -func (f *Functions) BeginQuery(target gl.Enum, query gl.Query) { - if !f.EXT_disjoint_timer_query_webgl2.IsNull() { - f.Ctx.Call("beginQuery", int(target), js.Value(query)) - } else { - f.EXT_disjoint_timer_query.Call("beginQueryEXT", int(target), js.Value(query)) - } -} -func (f *Functions) BindAttribLocation(p gl.Program, a gl.Attrib, name string) { - f.Ctx.Call("bindAttribLocation", js.Value(p), int(a), name) -} -func (f *Functions) BindBuffer(target gl.Enum, b gl.Buffer) { - f.Ctx.Call("bindBuffer", int(target), js.Value(b)) -} -func (f *Functions) BindBufferBase(target gl.Enum, index int, b gl.Buffer) { - f.Ctx.Call("bindBufferBase", int(target), index, js.Value(b)) -} -func (f *Functions) BindFramebuffer(target gl.Enum, fb gl.Framebuffer) { - f.Ctx.Call("bindFramebuffer", int(target), js.Value(fb)) -} -func (f *Functions) BindRenderbuffer(target gl.Enum, rb gl.Renderbuffer) { - f.Ctx.Call("bindRenderbuffer", int(target), js.Value(rb)) -} -func (f *Functions) BindTexture(target gl.Enum, t gl.Texture) { - f.Ctx.Call("bindTexture", int(target), js.Value(t)) -} -func (f *Functions) BlendEquation(mode gl.Enum) { - f.Ctx.Call("blendEquation", int(mode)) -} -func (f *Functions) BlendFunc(sfactor, dfactor gl.Enum) { - f.Ctx.Call("blendFunc", int(sfactor), int(dfactor)) -} -func (f *Functions) BufferData(target gl.Enum, src []byte, usage gl.Enum) { - f.Ctx.Call("bufferData", int(target), f.byteArrayOf(src), int(usage)) -} -func (f *Functions) CheckFramebufferStatus(target gl.Enum) gl.Enum { - return gl.Enum(f.Ctx.Call("checkFramebufferStatus", int(target)).Int()) -} -func (f *Functions) Clear(mask gl.Enum) { - f.Ctx.Call("clear", int(mask)) -} -func (f *Functions) ClearColor(red, green, blue, alpha float32) { - f.Ctx.Call("clearColor", red, green, blue, alpha) -} -func (f *Functions) ClearDepthf(d float32) { - f.Ctx.Call("clearDepth", d) -} -func (f *Functions) CompileShader(s gl.Shader) { - f.Ctx.Call("compileShader", js.Value(s)) -} -func (f *Functions) CreateBuffer() gl.Buffer { - return gl.Buffer(f.Ctx.Call("createBuffer")) -} -func (f *Functions) CreateFramebuffer() gl.Framebuffer { - return gl.Framebuffer(f.Ctx.Call("createFramebuffer")) -} -func (f *Functions) CreateProgram() gl.Program { - return gl.Program(f.Ctx.Call("createProgram")) -} -func (f *Functions) CreateQuery() gl.Query { - return gl.Query(f.Ctx.Call("createQuery")) -} -func (f *Functions) CreateRenderbuffer() gl.Renderbuffer { - return gl.Renderbuffer(f.Ctx.Call("createRenderbuffer")) -} -func (f *Functions) CreateShader(ty gl.Enum) gl.Shader { - return gl.Shader(f.Ctx.Call("createShader", int(ty))) -} -func (f *Functions) CreateTexture() gl.Texture { - return gl.Texture(f.Ctx.Call("createTexture")) -} -func (f *Functions) DeleteBuffer(v gl.Buffer) { - f.Ctx.Call("deleteBuffer", js.Value(v)) -} -func (f *Functions) DeleteFramebuffer(v gl.Framebuffer) { - f.Ctx.Call("deleteFramebuffer", js.Value(v)) -} -func (f *Functions) DeleteProgram(p gl.Program) { - f.Ctx.Call("deleteProgram", js.Value(p)) -} -func (f *Functions) DeleteQuery(query gl.Query) { - if !f.EXT_disjoint_timer_query_webgl2.IsNull() { - f.Ctx.Call("deleteQuery", js.Value(query)) - } else { - f.EXT_disjoint_timer_query.Call("deleteQueryEXT", js.Value(query)) - } -} -func (f *Functions) DeleteShader(s gl.Shader) { - f.Ctx.Call("deleteShader", js.Value(s)) -} -func (f *Functions) DeleteRenderbuffer(v gl.Renderbuffer) { - f.Ctx.Call("deleteRenderbuffer", js.Value(v)) -} -func (f *Functions) DeleteTexture(v gl.Texture) { - f.Ctx.Call("deleteTexture", js.Value(v)) -} -func (f *Functions) DepthFunc(fn gl.Enum) { - f.Ctx.Call("depthFunc", int(fn)) -} -func (f *Functions) DepthMask(mask bool) { - f.Ctx.Call("depthMask", mask) -} -func (f *Functions) DisableVertexAttribArray(a gl.Attrib) { - f.Ctx.Call("disableVertexAttribArray", int(a)) -} -func (f *Functions) Disable(cap gl.Enum) { - f.Ctx.Call("disable", int(cap)) -} -func (f *Functions) DrawArrays(mode gl.Enum, first, count int) { - f.Ctx.Call("drawArrays", int(mode), first, count) -} -func (f *Functions) DrawElements(mode gl.Enum, count int, ty gl.Enum, offset int) { - f.Ctx.Call("drawElements", int(mode), count, int(ty), offset) -} -func (f *Functions) Enable(cap gl.Enum) { - f.Ctx.Call("enable", int(cap)) -} -func (f *Functions) EnableVertexAttribArray(a gl.Attrib) { - f.Ctx.Call("enableVertexAttribArray", int(a)) -} -func (f *Functions) EndQuery(target gl.Enum) { - if !f.EXT_disjoint_timer_query_webgl2.IsNull() { - f.Ctx.Call("endQuery", int(target)) - } else { - f.EXT_disjoint_timer_query.Call("endQueryEXT", int(target)) - } -} -func (f *Functions) Finish() { - f.Ctx.Call("finish") -} -func (f *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget gl.Enum, renderbuffer gl.Renderbuffer) { - f.Ctx.Call("framebufferRenderbuffer", int(target), int(attachment), int(renderbuffertarget), js.Value(renderbuffer)) -} -func (f *Functions) FramebufferTexture2D(target, attachment, texTarget gl.Enum, t gl.Texture, level int) { - f.Ctx.Call("framebufferTexture2D", int(target), int(attachment), int(texTarget), js.Value(t), level) -} -func (f *Functions) GetError() gl.Enum { - return gl.Enum(f.Ctx.Call("getError").Int()) -} -func (f *Functions) GetRenderbufferParameteri(target, pname gl.Enum) int { - return paramVal(f.Ctx.Call("getRenderbufferParameteri", int(pname))) -} -func (f *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname gl.Enum) int { - return paramVal(f.Ctx.Call("getFramebufferAttachmentParameter", int(target), int(attachment), int(pname))) -} -func (f *Functions) GetBinding(pname gl.Enum) gl.Object { - return gl.Object(f.Ctx.Call("getParameter", int(pname))) -} -func (f *Functions) GetInteger(pname gl.Enum) int { - return paramVal(f.Ctx.Call("getParameter", int(pname))) -} -func (f *Functions) GetProgrami(p gl.Program, pname gl.Enum) int { - return paramVal(f.Ctx.Call("getProgramParameter", js.Value(p), int(pname))) -} -func (f *Functions) GetProgramInfoLog(p gl.Program) string { - return f.Ctx.Call("getProgramInfoLog", js.Value(p)).String() -} -func (f *Functions) GetQueryObjectuiv(query gl.Query, pname gl.Enum) uint { - if !f.EXT_disjoint_timer_query_webgl2.IsNull() { - return uint(paramVal(f.Ctx.Call("getQueryParameter", js.Value(query), int(pname)))) - } else { - return uint(paramVal(f.EXT_disjoint_timer_query.Call("getQueryObjectEXT", js.Value(query), int(pname)))) - } -} -func (f *Functions) GetShaderi(s gl.Shader, pname gl.Enum) int { - return paramVal(f.Ctx.Call("getShaderParameter", js.Value(s), int(pname))) -} -func (f *Functions) GetShaderInfoLog(s gl.Shader) string { - return f.Ctx.Call("getShaderInfoLog", js.Value(s)).String() -} -func (f *Functions) GetString(pname gl.Enum) string { - switch pname { - case gl.EXTENSIONS: - extsjs := f.Ctx.Call("getSupportedExtensions") - var exts []string - for i := 0; i < extsjs.Length(); i++ { - exts = append(exts, "GL_"+extsjs.Index(i).String()) - } - return strings.Join(exts, " ") - default: - return f.Ctx.Call("getParameter", int(pname)).String() - } -} -func (f *Functions) GetUniformBlockIndex(p gl.Program, name string) uint { - return uint(paramVal(f.Ctx.Call("getUniformBlockIndex", js.Value(p), name))) -} -func (f *Functions) GetUniformLocation(p gl.Program, name string) gl.Uniform { - return gl.Uniform(f.Ctx.Call("getUniformLocation", js.Value(p), name)) -} -func (f *Functions) InvalidateFramebuffer(target, attachment gl.Enum) { - fn := f.Ctx.Get("invalidateFramebuffer") - if !fn.IsUndefined() { - if f.int32Buf.IsUndefined() { - f.int32Buf = js.Global().Get("Int32Array").New(1) - } - f.int32Buf.SetIndex(0, int32(attachment)) - f.Ctx.Call("invalidateFramebuffer", int(target), f.int32Buf) - } -} -func (f *Functions) LinkProgram(p gl.Program) { - f.Ctx.Call("linkProgram", js.Value(p)) -} -func (f *Functions) PixelStorei(pname gl.Enum, param int32) { - f.Ctx.Call("pixelStorei", int(pname), param) -} -func (f *Functions) RenderbufferStorage(target, internalformat gl.Enum, width, height int) { - f.Ctx.Call("renderbufferStorage", int(target), int(internalformat), width, height) -} -func (f *Functions) ReadPixels(x, y, width, height int, format, ty gl.Enum, data []byte) { - f.resizeByteBuffer(len(data)) - f.Ctx.Call("readPixels", x, y, width, height, int(format), int(ty), f.byteBuf) - js.CopyBytesToGo(data, f.byteBuf) -} -func (f *Functions) Scissor(x, y, width, height int32) { - f.Ctx.Call("scissor", x, y, width, height) -} -func (f *Functions) ShaderSource(s gl.Shader, src string) { - f.Ctx.Call("shaderSource", js.Value(s), src) -} -func (f *Functions) TexImage2D(target gl.Enum, level int, internalFormat int, width, height int, format, ty gl.Enum, data []byte) { - f.Ctx.Call("texImage2D", int(target), int(level), int(internalFormat), int(width), int(height), 0, int(format), int(ty), f.byteArrayOf(data)) -} -func (f *Functions) TexSubImage2D(target gl.Enum, level int, x, y, width, height int, format, ty gl.Enum, data []byte) { - f.Ctx.Call("texSubImage2D", int(target), level, x, y, width, height, int(format), int(ty), f.byteArrayOf(data)) -} -func (f *Functions) TexParameteri(target, pname gl.Enum, param int) { - f.Ctx.Call("texParameteri", int(target), int(pname), int(param)) -} -func (f *Functions) UniformBlockBinding(p gl.Program, uniformBlockIndex uint, uniformBlockBinding uint) { - f.Ctx.Call("uniformBlockBinding", js.Value(p), int(uniformBlockIndex), int(uniformBlockBinding)) -} -func (f *Functions) Uniform1f(dst gl.Uniform, v float32) { - f.Ctx.Call("uniform1f", js.Value(dst), v) -} -func (f *Functions) Uniform1i(dst gl.Uniform, v int) { - f.Ctx.Call("uniform1i", js.Value(dst), v) -} -func (f *Functions) Uniform2f(dst gl.Uniform, v0, v1 float32) { - f.Ctx.Call("uniform2f", js.Value(dst), v0, v1) -} -func (f *Functions) Uniform3f(dst gl.Uniform, v0, v1, v2 float32) { - f.Ctx.Call("uniform3f", js.Value(dst), v0, v1, v2) -} -func (f *Functions) Uniform4f(dst gl.Uniform, v0, v1, v2, v3 float32) { - f.Ctx.Call("uniform4f", js.Value(dst), v0, v1, v2, v3) -} -func (f *Functions) UseProgram(p gl.Program) { - f.Ctx.Call("useProgram", js.Value(p)) -} -func (f *Functions) VertexAttribPointer(dst gl.Attrib, size int, ty gl.Enum, normalized bool, stride, offset int) { - f.Ctx.Call("vertexAttribPointer", int(dst), size, int(ty), normalized, stride, offset) -} -func (f *Functions) Viewport(x, y, width, height int) { - f.Ctx.Call("viewport", x, y, width, height) -} - -func (f *Functions) byteArrayOf(data []byte) js.Value { - if len(data) == 0 { - return js.Null() - } - f.resizeByteBuffer(len(data)) - js.CopyBytesToJS(f.byteBuf, data) - return f.byteBuf -} - -func (f *Functions) resizeByteBuffer(n int) { - if n == 0 { - return - } - if !f.byteBuf.IsUndefined() && f.byteBuf.Length() >= n { - return - } - f.byteBuf = js.Global().Get("Uint8Array").New(n) -} - -func paramVal(v js.Value) int { - switch v.Type() { - case js.TypeBoolean: - if b := v.Bool(); b { - return 1 - } else { - return 0 - } - case js.TypeNumber: - return v.Int() - default: - panic("unknown parameter type") - } -} diff --git a/vendor/gioui.org/app/internal/glimpl/gl_windows.go b/vendor/gioui.org/app/internal/glimpl/gl_windows.go deleted file mode 100644 index 4fe1d91..0000000 --- a/vendor/gioui.org/app/internal/glimpl/gl_windows.go +++ /dev/null @@ -1,406 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package glimpl - -import ( - "math" - "runtime" - "syscall" - "unsafe" - - "golang.org/x/sys/windows" - - "gioui.org/gpu/gl" - gunsafe "gioui.org/internal/unsafe" -) - -var ( - LibGLESv2 = windows.NewLazyDLL("libGLESv2.dll") - _glActiveTexture = LibGLESv2.NewProc("glActiveTexture") - _glAttachShader = LibGLESv2.NewProc("glAttachShader") - _glBeginQuery = LibGLESv2.NewProc("glBeginQuery") - _glBindAttribLocation = LibGLESv2.NewProc("glBindAttribLocation") - _glBindBuffer = LibGLESv2.NewProc("glBindBuffer") - _glBindBufferBase = LibGLESv2.NewProc("glBindBufferBase") - _glBindFramebuffer = LibGLESv2.NewProc("glBindFramebuffer") - _glBindRenderbuffer = LibGLESv2.NewProc("glBindRenderbuffer") - _glBindTexture = LibGLESv2.NewProc("glBindTexture") - _glBlendEquation = LibGLESv2.NewProc("glBlendEquation") - _glBlendFunc = LibGLESv2.NewProc("glBlendFunc") - _glBufferData = LibGLESv2.NewProc("glBufferData") - _glCheckFramebufferStatus = LibGLESv2.NewProc("glCheckFramebufferStatus") - _glClear = LibGLESv2.NewProc("glClear") - _glClearColor = LibGLESv2.NewProc("glClearColor") - _glClearDepthf = LibGLESv2.NewProc("glClearDepthf") - _glDeleteQueries = LibGLESv2.NewProc("glDeleteQueries") - _glCompileShader = LibGLESv2.NewProc("glCompileShader") - _glGenBuffers = LibGLESv2.NewProc("glGenBuffers") - _glGenFramebuffers = LibGLESv2.NewProc("glGenFramebuffers") - _glGetUniformBlockIndex = LibGLESv2.NewProc("glGetUniformBlockIndex") - _glCreateProgram = LibGLESv2.NewProc("glCreateProgram") - _glGenRenderbuffers = LibGLESv2.NewProc("glGenRenderbuffers") - _glCreateShader = LibGLESv2.NewProc("glCreateShader") - _glGenTextures = LibGLESv2.NewProc("glGenTextures") - _glDeleteBuffers = LibGLESv2.NewProc("glDeleteBuffers") - _glDeleteFramebuffers = LibGLESv2.NewProc("glDeleteFramebuffers") - _glDeleteProgram = LibGLESv2.NewProc("glDeleteProgram") - _glDeleteShader = LibGLESv2.NewProc("glDeleteShader") - _glDeleteRenderbuffers = LibGLESv2.NewProc("glDeleteRenderbuffers") - _glDeleteTextures = LibGLESv2.NewProc("glDeleteTextures") - _glDepthFunc = LibGLESv2.NewProc("glDepthFunc") - _glDepthMask = LibGLESv2.NewProc("glDepthMask") - _glDisableVertexAttribArray = LibGLESv2.NewProc("glDisableVertexAttribArray") - _glDisable = LibGLESv2.NewProc("glDisable") - _glDrawArrays = LibGLESv2.NewProc("glDrawArrays") - _glDrawElements = LibGLESv2.NewProc("glDrawElements") - _glEnable = LibGLESv2.NewProc("glEnable") - _glEnableVertexAttribArray = LibGLESv2.NewProc("glEnableVertexAttribArray") - _glEndQuery = LibGLESv2.NewProc("glEndQuery") - _glFinish = LibGLESv2.NewProc("glFinish") - _glFramebufferRenderbuffer = LibGLESv2.NewProc("glFramebufferRenderbuffer") - _glFramebufferTexture2D = LibGLESv2.NewProc("glFramebufferTexture2D") - _glGenQueries = LibGLESv2.NewProc("glGenQueries") - _glGetError = LibGLESv2.NewProc("glGetError") - _glGetRenderbufferParameteri = LibGLESv2.NewProc("glGetRenderbufferParameteri") - _glGetFramebufferAttachmentParameteri = LibGLESv2.NewProc("glGetFramebufferAttachmentParameteri") - _glGetIntegerv = LibGLESv2.NewProc("glGetIntegerv") - _glGetProgramiv = LibGLESv2.NewProc("glGetProgramiv") - _glGetProgramInfoLog = LibGLESv2.NewProc("glGetProgramInfoLog") - _glGetQueryObjectuiv = LibGLESv2.NewProc("glGetQueryObjectuiv") - _glGetShaderiv = LibGLESv2.NewProc("glGetShaderiv") - _glGetShaderInfoLog = LibGLESv2.NewProc("glGetShaderInfoLog") - _glGetString = LibGLESv2.NewProc("glGetString") - _glGetUniformLocation = LibGLESv2.NewProc("glGetUniformLocation") - _glInvalidateFramebuffer = LibGLESv2.NewProc("glInvalidateFramebuffer") - _glLinkProgram = LibGLESv2.NewProc("glLinkProgram") - _glPixelStorei = LibGLESv2.NewProc("glPixelStorei") - _glReadPixels = LibGLESv2.NewProc("glReadPixels") - _glRenderbufferStorage = LibGLESv2.NewProc("glRenderbufferStorage") - _glScissor = LibGLESv2.NewProc("glScissor") - _glShaderSource = LibGLESv2.NewProc("glShaderSource") - _glTexImage2D = LibGLESv2.NewProc("glTexImage2D") - _glTexSubImage2D = LibGLESv2.NewProc("glTexSubImage2D") - _glTexParameteri = LibGLESv2.NewProc("glTexParameteri") - _glUniformBlockBinding = LibGLESv2.NewProc("glUniformBlockBinding") - _glUniform1f = LibGLESv2.NewProc("glUniform1f") - _glUniform1i = LibGLESv2.NewProc("glUniform1i") - _glUniform2f = LibGLESv2.NewProc("glUniform2f") - _glUniform3f = LibGLESv2.NewProc("glUniform3f") - _glUniform4f = LibGLESv2.NewProc("glUniform4f") - _glUseProgram = LibGLESv2.NewProc("glUseProgram") - _glVertexAttribPointer = LibGLESv2.NewProc("glVertexAttribPointer") - _glViewport = LibGLESv2.NewProc("glViewport") -) - -type Functions struct { - // gl.Query caches. - int32s [100]int32 -} - -func (c *Functions) ActiveTexture(t gl.Enum) { - syscall.Syscall(_glActiveTexture.Addr(), 1, uintptr(t), 0, 0) -} -func (c *Functions) AttachShader(p gl.Program, s gl.Shader) { - syscall.Syscall(_glAttachShader.Addr(), 2, uintptr(p.V), uintptr(s.V), 0) -} -func (f *Functions) BeginQuery(target gl.Enum, query gl.Query) { - syscall.Syscall(_glBeginQuery.Addr(), 2, uintptr(target), uintptr(query.V), 0) -} -func (c *Functions) BindAttribLocation(p gl.Program, a gl.Attrib, name string) { - cname := cString(name) - c0 := &cname[0] - syscall.Syscall(_glBindAttribLocation.Addr(), 3, uintptr(p.V), uintptr(a), uintptr(unsafe.Pointer(c0))) - issue34474KeepAlive(c) -} -func (c *Functions) BindBuffer(target gl.Enum, b gl.Buffer) { - syscall.Syscall(_glBindBuffer.Addr(), 2, uintptr(target), uintptr(b.V), 0) -} -func (c *Functions) BindBufferBase(target gl.Enum, index int, b gl.Buffer) { - syscall.Syscall(_glBindBufferBase.Addr(), 3, uintptr(target), uintptr(index), uintptr(b.V)) -} -func (c *Functions) BindFramebuffer(target gl.Enum, fb gl.Framebuffer) { - syscall.Syscall(_glBindFramebuffer.Addr(), 2, uintptr(target), uintptr(fb.V), 0) -} -func (c *Functions) BindRenderbuffer(target gl.Enum, rb gl.Renderbuffer) { - syscall.Syscall(_glBindRenderbuffer.Addr(), 2, uintptr(target), uintptr(rb.V), 0) -} -func (c *Functions) BindTexture(target gl.Enum, t gl.Texture) { - syscall.Syscall(_glBindTexture.Addr(), 2, uintptr(target), uintptr(t.V), 0) -} -func (c *Functions) BlendEquation(mode gl.Enum) { - syscall.Syscall(_glBlendEquation.Addr(), 1, uintptr(mode), 0, 0) -} -func (c *Functions) BlendFunc(sfactor, dfactor gl.Enum) { - syscall.Syscall(_glBlendFunc.Addr(), 2, uintptr(sfactor), uintptr(dfactor), 0) -} -func (c *Functions) BufferData(target gl.Enum, src []byte, usage gl.Enum) { - if n := len(src); n == 0 { - syscall.Syscall6(_glBufferData.Addr(), 4, uintptr(target), 0, 0, uintptr(usage), 0, 0) - } else { - s0 := &src[0] - syscall.Syscall6(_glBufferData.Addr(), 4, uintptr(target), uintptr(n), uintptr(unsafe.Pointer(s0)), uintptr(usage), 0, 0) - issue34474KeepAlive(s0) - } -} -func (c *Functions) CheckFramebufferStatus(target gl.Enum) gl.Enum { - s, _, _ := syscall.Syscall(_glCheckFramebufferStatus.Addr(), 1, uintptr(target), 0, 0) - return gl.Enum(s) -} -func (c *Functions) Clear(mask gl.Enum) { - syscall.Syscall(_glClear.Addr(), 1, uintptr(mask), 0, 0) -} -func (c *Functions) ClearColor(red, green, blue, alpha float32) { - syscall.Syscall6(_glClearColor.Addr(), 4, uintptr(math.Float32bits(red)), uintptr(math.Float32bits(green)), uintptr(math.Float32bits(blue)), uintptr(math.Float32bits(alpha)), 0, 0) -} -func (c *Functions) ClearDepthf(d float32) { - syscall.Syscall(_glClearDepthf.Addr(), 1, uintptr(math.Float32bits(d)), 0, 0) -} -func (c *Functions) CompileShader(s gl.Shader) { - syscall.Syscall(_glCompileShader.Addr(), 1, uintptr(s.V), 0, 0) -} -func (c *Functions) CreateBuffer() gl.Buffer { - var buf uintptr - syscall.Syscall(_glGenBuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&buf)), 0) - return gl.Buffer{uint(buf)} -} -func (c *Functions) CreateFramebuffer() gl.Framebuffer { - var fb uintptr - syscall.Syscall(_glGenFramebuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&fb)), 0) - return gl.Framebuffer{uint(fb)} -} -func (c *Functions) CreateProgram() gl.Program { - p, _, _ := syscall.Syscall(_glCreateProgram.Addr(), 0, 0, 0, 0) - return gl.Program{uint(p)} -} -func (f *Functions) CreateQuery() gl.Query { - var q uintptr - syscall.Syscall(_glGenQueries.Addr(), 2, 1, uintptr(unsafe.Pointer(&q)), 0) - return gl.Query{uint(q)} -} -func (c *Functions) CreateRenderbuffer() gl.Renderbuffer { - var rb uintptr - syscall.Syscall(_glGenRenderbuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&rb)), 0) - return gl.Renderbuffer{uint(rb)} -} -func (c *Functions) CreateShader(ty gl.Enum) gl.Shader { - s, _, _ := syscall.Syscall(_glCreateShader.Addr(), 1, uintptr(ty), 0, 0) - return gl.Shader{uint(s)} -} -func (c *Functions) CreateTexture() gl.Texture { - var t uintptr - syscall.Syscall(_glGenTextures.Addr(), 2, 1, uintptr(unsafe.Pointer(&t)), 0) - return gl.Texture{uint(t)} -} -func (c *Functions) DeleteBuffer(v gl.Buffer) { - syscall.Syscall(_glDeleteBuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v)), 0) -} -func (c *Functions) DeleteFramebuffer(v gl.Framebuffer) { - syscall.Syscall(_glDeleteFramebuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0) -} -func (c *Functions) DeleteProgram(p gl.Program) { - syscall.Syscall(_glDeleteProgram.Addr(), 1, uintptr(p.V), 0, 0) -} -func (f *Functions) DeleteQuery(query gl.Query) { - syscall.Syscall(_glDeleteQueries.Addr(), 2, 1, uintptr(unsafe.Pointer(&query.V)), 0) -} -func (c *Functions) DeleteShader(s gl.Shader) { - syscall.Syscall(_glDeleteShader.Addr(), 1, uintptr(s.V), 0, 0) -} -func (c *Functions) DeleteRenderbuffer(v gl.Renderbuffer) { - syscall.Syscall(_glDeleteRenderbuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0) -} -func (c *Functions) DeleteTexture(v gl.Texture) { - syscall.Syscall(_glDeleteTextures.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0) -} -func (c *Functions) DepthFunc(f gl.Enum) { - syscall.Syscall(_glDepthFunc.Addr(), 1, uintptr(f), 0, 0) -} -func (c *Functions) DepthMask(mask bool) { - var m uintptr - if mask { - m = 1 - } - syscall.Syscall(_glDepthMask.Addr(), 1, m, 0, 0) -} -func (c *Functions) DisableVertexAttribArray(a gl.Attrib) { - syscall.Syscall(_glDisableVertexAttribArray.Addr(), 1, uintptr(a), 0, 0) -} -func (c *Functions) Disable(cap gl.Enum) { - syscall.Syscall(_glDisable.Addr(), 1, uintptr(cap), 0, 0) -} -func (c *Functions) DrawArrays(mode gl.Enum, first, count int) { - syscall.Syscall(_glDrawArrays.Addr(), 3, uintptr(mode), uintptr(first), uintptr(count)) -} -func (c *Functions) DrawElements(mode gl.Enum, count int, ty gl.Enum, offset int) { - syscall.Syscall6(_glDrawElements.Addr(), 4, uintptr(mode), uintptr(count), uintptr(ty), uintptr(offset), 0, 0) -} -func (c *Functions) Enable(cap gl.Enum) { - syscall.Syscall(_glEnable.Addr(), 1, uintptr(cap), 0, 0) -} -func (c *Functions) EnableVertexAttribArray(a gl.Attrib) { - syscall.Syscall(_glEnableVertexAttribArray.Addr(), 1, uintptr(a), 0, 0) -} -func (f *Functions) EndQuery(target gl.Enum) { - syscall.Syscall(_glEndQuery.Addr(), 1, uintptr(target), 0, 0) -} -func (c *Functions) Finish() { - syscall.Syscall(_glFinish.Addr(), 0, 0, 0, 0) -} -func (c *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget gl.Enum, renderbuffer gl.Renderbuffer) { - syscall.Syscall6(_glFramebufferRenderbuffer.Addr(), 4, uintptr(target), uintptr(attachment), uintptr(renderbuffertarget), uintptr(renderbuffer.V), 0, 0) -} -func (c *Functions) FramebufferTexture2D(target, attachment, texTarget gl.Enum, t gl.Texture, level int) { - syscall.Syscall6(_glFramebufferTexture2D.Addr(), 5, uintptr(target), uintptr(attachment), uintptr(texTarget), uintptr(t.V), uintptr(level), 0) -} -func (f *Functions) GetUniformBlockIndex(p gl.Program, name string) uint { - cname := cString(name) - c0 := &cname[0] - u, _, _ := syscall.Syscall(_glGetUniformBlockIndex.Addr(), 2, uintptr(p.V), uintptr(unsafe.Pointer(c0)), 0) - issue34474KeepAlive(c0) - return uint(u) -} -func (c *Functions) GetBinding(pname gl.Enum) gl.Object { - return gl.Object{uint(c.GetInteger(pname))} -} -func (c *Functions) GetError() gl.Enum { - e, _, _ := syscall.Syscall(_glGetError.Addr(), 0, 0, 0, 0) - return gl.Enum(e) -} -func (c *Functions) GetRenderbufferParameteri(target, pname gl.Enum) int { - p, _, _ := syscall.Syscall(_glGetRenderbufferParameteri.Addr(), 2, uintptr(target), uintptr(pname), 0) - return int(p) -} -func (c *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname gl.Enum) int { - p, _, _ := syscall.Syscall(_glGetFramebufferAttachmentParameteri.Addr(), 3, uintptr(target), uintptr(attachment), uintptr(pname)) - return int(p) -} -func (c *Functions) GetInteger(pname gl.Enum) int { - syscall.Syscall(_glGetIntegerv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0) - return int(c.int32s[0]) -} -func (c *Functions) GetProgrami(p gl.Program, pname gl.Enum) int { - syscall.Syscall(_glGetProgramiv.Addr(), 3, uintptr(p.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0]))) - return int(c.int32s[0]) -} -func (c *Functions) GetProgramInfoLog(p gl.Program) string { - n := c.GetProgrami(p, gl.INFO_LOG_LENGTH) - buf := make([]byte, n) - syscall.Syscall6(_glGetProgramInfoLog.Addr(), 4, uintptr(p.V), uintptr(len(buf)), 0, uintptr(unsafe.Pointer(&buf[0])), 0, 0) - return string(buf) -} -func (c *Functions) GetQueryObjectuiv(query gl.Query, pname gl.Enum) uint { - syscall.Syscall(_glGetQueryObjectuiv.Addr(), 3, uintptr(query.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0]))) - return uint(c.int32s[0]) -} -func (c *Functions) GetShaderi(s gl.Shader, pname gl.Enum) int { - syscall.Syscall(_glGetShaderiv.Addr(), 3, uintptr(s.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0]))) - return int(c.int32s[0]) -} -func (c *Functions) GetShaderInfoLog(s gl.Shader) string { - n := c.GetShaderi(s, gl.INFO_LOG_LENGTH) - buf := make([]byte, n) - syscall.Syscall6(_glGetShaderInfoLog.Addr(), 4, uintptr(s.V), uintptr(len(buf)), 0, uintptr(unsafe.Pointer(&buf[0])), 0, 0) - return string(buf) -} -func (c *Functions) GetString(pname gl.Enum) string { - s, _, _ := syscall.Syscall(_glGetString.Addr(), 1, uintptr(pname), 0, 0) - return gunsafe.GoString(gunsafe.SliceOf(s)) -} -func (c *Functions) GetUniformLocation(p gl.Program, name string) gl.Uniform { - cname := cString(name) - c0 := &cname[0] - u, _, _ := syscall.Syscall(_glGetUniformLocation.Addr(), 2, uintptr(p.V), uintptr(unsafe.Pointer(c0)), 0) - issue34474KeepAlive(c0) - return gl.Uniform{int(u)} -} -func (c *Functions) InvalidateFramebuffer(target, attachment gl.Enum) { - addr := _glInvalidateFramebuffer.Addr() - if addr == 0 { - // InvalidateFramebuffer is just a hint. Skip it if not supported. - return - } - syscall.Syscall(addr, 3, uintptr(target), 1, uintptr(unsafe.Pointer(&attachment))) -} -func (c *Functions) LinkProgram(p gl.Program) { - syscall.Syscall(_glLinkProgram.Addr(), 1, uintptr(p.V), 0, 0) -} -func (c *Functions) PixelStorei(pname gl.Enum, param int32) { - syscall.Syscall(_glPixelStorei.Addr(), 2, uintptr(pname), uintptr(param), 0) -} -func (f *Functions) ReadPixels(x, y, width, height int, format, ty gl.Enum, data []byte) { - d0 := &data[0] - syscall.Syscall9(_glReadPixels.Addr(), 7, uintptr(x), uintptr(y), uintptr(width), uintptr(height), uintptr(format), uintptr(ty), uintptr(unsafe.Pointer(d0)), 0, 0) - issue34474KeepAlive(d0) -} -func (c *Functions) RenderbufferStorage(target, internalformat gl.Enum, width, height int) { - syscall.Syscall6(_glRenderbufferStorage.Addr(), 4, uintptr(target), uintptr(internalformat), uintptr(width), uintptr(height), 0, 0) -} -func (c *Functions) Scissor(x, y, width, height int32) { - syscall.Syscall6(_glScissor.Addr(), 4, uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0, 0) -} -func (c *Functions) ShaderSource(s gl.Shader, src string) { - var n uintptr = uintptr(len(src)) - psrc := &src - syscall.Syscall6(_glShaderSource.Addr(), 4, uintptr(s.V), 1, uintptr(unsafe.Pointer(psrc)), uintptr(unsafe.Pointer(&n)), 0, 0) - issue34474KeepAlive(psrc) -} -func (c *Functions) TexImage2D(target gl.Enum, level int, internalFormat int, width, height int, format, ty gl.Enum, data []byte) { - if len(data) == 0 { - syscall.Syscall9(_glTexImage2D.Addr(), 9, uintptr(target), uintptr(level), uintptr(internalFormat), uintptr(width), uintptr(height), 0, uintptr(format), uintptr(ty), 0) - } else { - d0 := &data[0] - syscall.Syscall9(_glTexImage2D.Addr(), 9, uintptr(target), uintptr(level), uintptr(internalFormat), uintptr(width), uintptr(height), 0, uintptr(format), uintptr(ty), uintptr(unsafe.Pointer(d0))) - issue34474KeepAlive(d0) - } -} -func (c *Functions) TexSubImage2D(target gl.Enum, level int, x, y, width, height int, format, ty gl.Enum, data []byte) { - d0 := &data[0] - syscall.Syscall9(_glTexSubImage2D.Addr(), 9, uintptr(target), uintptr(level), uintptr(x), uintptr(y), uintptr(width), uintptr(height), uintptr(format), uintptr(ty), uintptr(unsafe.Pointer(d0))) - issue34474KeepAlive(d0) -} -func (c *Functions) TexParameteri(target, pname gl.Enum, param int) { - syscall.Syscall(_glTexParameteri.Addr(), 3, uintptr(target), uintptr(pname), uintptr(param)) -} -func (f *Functions) UniformBlockBinding(p gl.Program, uniformBlockIndex uint, uniformBlockBinding uint) { - syscall.Syscall(_glUniformBlockBinding.Addr(), 3, uintptr(p.V), uintptr(uniformBlockIndex), uintptr(uniformBlockBinding)) -} -func (c *Functions) Uniform1f(dst gl.Uniform, v float32) { - syscall.Syscall(_glUniform1f.Addr(), 2, uintptr(dst.V), uintptr(math.Float32bits(v)), 0) -} -func (c *Functions) Uniform1i(dst gl.Uniform, v int) { - syscall.Syscall(_glUniform1i.Addr(), 2, uintptr(dst.V), uintptr(v), 0) -} -func (c *Functions) Uniform2f(dst gl.Uniform, v0, v1 float32) { - syscall.Syscall(_glUniform2f.Addr(), 3, uintptr(dst.V), uintptr(math.Float32bits(v0)), uintptr(math.Float32bits(v1))) -} -func (c *Functions) Uniform3f(dst gl.Uniform, v0, v1, v2 float32) { - syscall.Syscall6(_glUniform3f.Addr(), 4, uintptr(dst.V), uintptr(math.Float32bits(v0)), uintptr(math.Float32bits(v1)), uintptr(math.Float32bits(v2)), 0, 0) -} -func (c *Functions) Uniform4f(dst gl.Uniform, v0, v1, v2, v3 float32) { - syscall.Syscall6(_glUniform4f.Addr(), 5, uintptr(dst.V), uintptr(math.Float32bits(v0)), uintptr(math.Float32bits(v1)), uintptr(math.Float32bits(v2)), uintptr(math.Float32bits(v3)), 0) -} -func (c *Functions) UseProgram(p gl.Program) { - syscall.Syscall(_glUseProgram.Addr(), 1, uintptr(p.V), 0, 0) -} -func (c *Functions) VertexAttribPointer(dst gl.Attrib, size int, ty gl.Enum, normalized bool, stride, offset int) { - var norm uintptr - if normalized { - norm = 1 - } - syscall.Syscall6(_glVertexAttribPointer.Addr(), 6, uintptr(dst), uintptr(size), uintptr(ty), norm, uintptr(stride), uintptr(offset)) -} -func (c *Functions) Viewport(x, y, width, height int) { - syscall.Syscall6(_glViewport.Addr(), 4, uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0, 0) -} - -func cString(s string) []byte { - b := make([]byte, len(s)+1) - copy(b, s) - return b -} - -// issue34474KeepAlive calls runtime.KeepAlive as a -// workaround for golang.org/issue/34474. -func issue34474KeepAlive(v interface{}) { - runtime.KeepAlive(v) -} diff --git a/vendor/gioui.org/app/internal/log/log_android.go b/vendor/gioui.org/app/internal/log/log_android.go index 7936911..1245598 100644 --- a/vendor/gioui.org/app/internal/log/log_android.go +++ b/vendor/gioui.org/app/internal/log/log_android.go @@ -40,16 +40,22 @@ type androidLogWriter struct { } func (w *androidLogWriter) Write(data []byte) (int, error) { - // Truncate the buffer, leaving space for the '\0'. - if max := len(w.buf) - 1; len(data) > max { - data = data[:max] + n := 0 + for len(data) > 0 { + msg := data + // Truncate the buffer, leaving space for the '\0'. + if max := len(w.buf) - 1; len(msg) > max { + msg = msg[:max] + } + buf := w.buf[:len(msg)+1] + copy(buf, msg) + // Terminating '\0'. + buf[len(msg)] = 0 + C.__android_log_write(C.ANDROID_LOG_INFO, logTag, (*C.char)(unsafe.Pointer(&buf[0]))) + n += len(msg) + data = data[len(msg):] } - buf := w.buf[:len(data)+1] - copy(buf, data) - // Terminating '\0'. - buf[len(data)] = 0 - C.__android_log_write(C.ANDROID_LOG_INFO, logTag, (*C.char)(unsafe.Pointer(&buf[0]))) - return len(data), nil + return n, nil } func logFd(fd uintptr) { diff --git a/vendor/gioui.org/app/internal/log/log_ios.go b/vendor/gioui.org/app/internal/log/log_ios.go index e49f425..cad4e19 100644 --- a/vendor/gioui.org/app/internal/log/log_ios.go +++ b/vendor/gioui.org/app/internal/log/log_ios.go @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Unlicense OR MIT +//go:build darwin && ios // +build darwin,ios package log @@ -7,7 +8,11 @@ package log /* #cgo CFLAGS: -Werror -fmodules -fobjc-arc -x objective-c -__attribute__ ((visibility ("hidden"))) void nslog(char *str); +@import Foundation; + +static void nslog(char *str) { + NSLog(@"%@", @(str)); +} */ import "C" @@ -17,7 +22,7 @@ import ( "log" "unsafe" - _ "gioui.org/app/internal/cocoainit" + _ "gioui.org/internal/cocoainit" ) func init() { diff --git a/vendor/gioui.org/app/internal/log/log_ios.m b/vendor/gioui.org/app/internal/log/log_ios.m deleted file mode 100644 index 201bc36..0000000 --- a/vendor/gioui.org/app/internal/log/log_ios.m +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -// +build darwin,ios - -@import Foundation; - -#include "_cgo_export.h" - -void nslog(char *str) { - NSLog(@"%@", @(str)); -} diff --git a/vendor/gioui.org/app/internal/srgb/srgb.go b/vendor/gioui.org/app/internal/srgb/srgb.go deleted file mode 100644 index c148b04..0000000 --- a/vendor/gioui.org/app/internal/srgb/srgb.go +++ /dev/null @@ -1,195 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package srgb - -import ( - "fmt" - "runtime" - "strings" - - "gioui.org/app/internal/glimpl" - "gioui.org/gpu/gl" - "gioui.org/internal/unsafe" -) - -// FBO implements an intermediate sRGB FBO -// for gamma-correct rendering on platforms without -// sRGB enabled native framebuffers. -type FBO struct { - c *glimpl.Functions - width, height int - frameBuffer gl.Framebuffer - depthBuffer gl.Renderbuffer - colorTex gl.Texture - blitted bool - quad gl.Buffer - prog gl.Program - gl3 bool -} - -func New(f *glimpl.Functions) (*FBO, error) { - var gl3 bool - glVer := f.GetString(gl.VERSION) - ver, _, err := gl.ParseGLVersion(glVer) - if err != nil { - return nil, err - } - if ver[0] >= 3 { - gl3 = true - } else { - exts := f.GetString(gl.EXTENSIONS) - if !strings.Contains(exts, "EXT_sRGB") { - return nil, fmt.Errorf("no support for OpenGL ES 3 nor EXT_sRGB") - } - } - s := &FBO{ - c: f, - gl3: gl3, - frameBuffer: f.CreateFramebuffer(), - colorTex: f.CreateTexture(), - depthBuffer: f.CreateRenderbuffer(), - } - f.BindTexture(gl.TEXTURE_2D, s.colorTex) - f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) - f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) - f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) - f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) - return s, nil -} - -func (s *FBO) Blit() { - if !s.blitted { - prog, err := gl.CreateProgram(s.c, blitVSrc, blitFSrc, []string{"pos", "uv"}) - if err != nil { - panic(err) - } - s.prog = prog - s.c.UseProgram(prog) - s.c.Uniform1i(gl.GetUniformLocation(s.c, prog, "tex"), 0) - s.quad = s.c.CreateBuffer() - s.c.BindBuffer(gl.ARRAY_BUFFER, s.quad) - s.c.BufferData(gl.ARRAY_BUFFER, - unsafe.BytesView([]float32{ - -1, +1, 0, 1, - +1, +1, 1, 1, - -1, -1, 0, 0, - +1, -1, 1, 0, - }), - gl.STATIC_DRAW) - s.blitted = true - } - s.c.BindFramebuffer(gl.FRAMEBUFFER, gl.Framebuffer{}) - s.c.UseProgram(s.prog) - s.c.BindTexture(gl.TEXTURE_2D, s.colorTex) - s.c.BindBuffer(gl.ARRAY_BUFFER, s.quad) - s.c.VertexAttribPointer(0 /* pos */, 2, gl.FLOAT, false, 4*4, 0) - s.c.VertexAttribPointer(1 /* uv */, 2, gl.FLOAT, false, 4*4, 4*2) - s.c.EnableVertexAttribArray(0) - s.c.EnableVertexAttribArray(1) - s.c.DrawArrays(gl.TRIANGLE_STRIP, 0, 4) - s.c.BindTexture(gl.TEXTURE_2D, gl.Texture{}) - s.c.DisableVertexAttribArray(0) - s.c.DisableVertexAttribArray(1) - s.c.BindFramebuffer(gl.FRAMEBUFFER, s.frameBuffer) - s.c.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0) - s.c.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT) - // The Android emulator requires framebuffer 0 bound at eglSwapBuffer time. - // Bind the sRGB framebuffer again in afterPresent. - s.c.BindFramebuffer(gl.FRAMEBUFFER, gl.Framebuffer{}) -} - -func (s *FBO) AfterPresent() { - s.c.BindFramebuffer(gl.FRAMEBUFFER, s.frameBuffer) -} - -func (s *FBO) Refresh(w, h int) error { - s.width, s.height = w, h - if w == 0 || h == 0 { - return nil - } - s.c.BindTexture(gl.TEXTURE_2D, s.colorTex) - if s.gl3 { - s.c.TexImage2D(gl.TEXTURE_2D, 0, gl.SRGB8_ALPHA8, w, h, gl.RGBA, gl.UNSIGNED_BYTE, nil) - } else /* EXT_sRGB */ { - s.c.TexImage2D(gl.TEXTURE_2D, 0, gl.SRGB_ALPHA_EXT, w, h, gl.SRGB_ALPHA_EXT, gl.UNSIGNED_BYTE, nil) - } - currentRB := gl.Renderbuffer(s.c.GetBinding(gl.RENDERBUFFER_BINDING)) - s.c.BindRenderbuffer(gl.RENDERBUFFER, s.depthBuffer) - s.c.RenderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, w, h) - s.c.BindRenderbuffer(gl.RENDERBUFFER, currentRB) - s.c.BindFramebuffer(gl.FRAMEBUFFER, s.frameBuffer) - s.c.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, s.colorTex, 0) - s.c.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, s.depthBuffer) - if st := s.c.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE { - return fmt.Errorf("sRGB framebuffer incomplete (%dx%d), status: %#x error: %x", s.width, s.height, st, s.c.GetError()) - } - - if runtime.GOOS == "js" { - // With macOS Safari, rendering to and then reading from a SRGB8_ALPHA8 - // texture result in twice gamma corrected colors. Using a plain RGBA - // texture seems to work. - s.c.ClearColor(.5, .5, .5, 1.0) - s.c.Clear(gl.COLOR_BUFFER_BIT) - var pixel [4]byte - s.c.ReadPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel[:]) - if pixel[0] == 128 { // Correct sRGB color value is ~188 - s.c.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w, h, gl.RGBA, gl.UNSIGNED_BYTE, nil) - if st := s.c.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE { - return fmt.Errorf("fallback RGBA framebuffer incomplete (%dx%d), status: %#x error: %x", s.width, s.height, st, s.c.GetError()) - } - } - } - - return nil -} - -func (s *FBO) Release() { - s.c.DeleteFramebuffer(s.frameBuffer) - s.c.DeleteTexture(s.colorTex) - s.c.DeleteRenderbuffer(s.depthBuffer) - if s.blitted { - s.c.DeleteBuffer(s.quad) - s.c.DeleteProgram(s.prog) - } - s.c = nil -} - -const ( - blitVSrc = ` -#version 100 - -precision highp float; - -attribute vec2 pos; -attribute vec2 uv; - -varying vec2 vUV; - -void main() { - gl_Position = vec4(pos, 0, 1); - vUV = uv; -} -` - blitFSrc = ` -#version 100 - -precision mediump float; - -uniform sampler2D tex; -varying vec2 vUV; - -vec3 gamma(vec3 rgb) { - vec3 exp = vec3(1.055)*pow(rgb, vec3(0.41666)) - vec3(0.055); - vec3 lin = rgb * vec3(12.92); - bvec3 cut = lessThan(rgb, vec3(0.0031308)); - return vec3(cut.r ? lin.r : exp.r, cut.g ? lin.g : exp.g, cut.b ? lin.b : exp.b); -} - -void main() { - vec4 col = texture2D(tex, vUV); - vec3 rgb = col.rgb; - rgb = gamma(rgb); - gl_FragColor = vec4(rgb, col.a); -} -` -) diff --git a/vendor/gioui.org/app/internal/window/GioView.java b/vendor/gioui.org/app/internal/window/GioView.java deleted file mode 100644 index be90c50..0000000 --- a/vendor/gioui.org/app/internal/window/GioView.java +++ /dev/null @@ -1,255 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package org.gioui; - -import java.lang.Class; -import java.lang.IllegalAccessException; -import java.lang.InstantiationException; -import java.lang.ExceptionInInitializerError; -import java.lang.SecurityException; -import android.app.Activity; -import android.app.Fragment; -import android.app.FragmentManager; -import android.app.FragmentTransaction; -import android.content.Context; -import android.graphics.Rect; -import android.os.Build; -import android.text.Editable; -import android.util.AttributeSet; -import android.util.TypedValue; -import android.view.Choreographer; -import android.view.KeyCharacterMap; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.WindowInsets; -import android.view.Surface; -import android.view.SurfaceView; -import android.view.SurfaceHolder; -import android.view.inputmethod.BaseInputConnection; -import android.view.inputmethod.InputConnection; -import android.view.inputmethod.InputMethodManager; -import android.view.inputmethod.EditorInfo; - -import java.io.UnsupportedEncodingException; - -public final class GioView extends SurfaceView implements Choreographer.FrameCallback { - private final static Object initLock = new Object(); - private static boolean jniLoaded; - - private final SurfaceHolder.Callback surfCallbacks; - private final View.OnFocusChangeListener focusCallback; - private final InputMethodManager imm; - private final float scrollXScale; - private final float scrollYScale; - - private long nhandle; - - public GioView(Context context) { - this(context, null); - } - - public GioView(Context context, AttributeSet attrs) { - super(context, attrs); - - // Late initialization of the Go runtime to wait for a valid context. - Gio.init(context.getApplicationContext()); - - ViewConfiguration conf = ViewConfiguration.get(context); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - scrollXScale = conf.getScaledHorizontalScrollFactor(); - scrollYScale = conf.getScaledVerticalScrollFactor(); - } else { - float listItemHeight = 48; // dp - float px = TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, - listItemHeight, - getResources().getDisplayMetrics() - ); - scrollXScale = px; - scrollYScale = px; - } - - nhandle = onCreateView(this); - imm = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE); - setFocusable(true); - setFocusableInTouchMode(true); - focusCallback = new View.OnFocusChangeListener() { - @Override public void onFocusChange(View v, boolean focus) { - GioView.this.onFocusChange(nhandle, focus); - } - }; - setOnFocusChangeListener(focusCallback); - surfCallbacks = new SurfaceHolder.Callback() { - @Override public void surfaceCreated(SurfaceHolder holder) { - // Ignore; surfaceChanged is guaranteed to be called immediately after this. - } - @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - onSurfaceChanged(nhandle, getHolder().getSurface()); - } - @Override public void surfaceDestroyed(SurfaceHolder holder) { - onSurfaceDestroyed(nhandle); - } - }; - getHolder().addCallback(surfCallbacks); - } - - @Override public boolean onKeyDown(int keyCode, KeyEvent event) { - onKeyEvent(nhandle, keyCode, event.getUnicodeChar(), event.getEventTime()); - return false; - } - - @Override public boolean onGenericMotionEvent(MotionEvent event) { - dispatchMotionEvent(event); - return true; - } - - @Override public boolean onTouchEvent(MotionEvent event) { - // Ask for unbuffered events. Flutter and Chrome does it - // so I assume its good for us as well. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - requestUnbufferedDispatch(event); - } - - dispatchMotionEvent(event); - return true; - } - - private void dispatchMotionEvent(MotionEvent event) { - for (int j = 0; j < event.getHistorySize(); j++) { - long time = event.getHistoricalEventTime(j); - for (int i = 0; i < event.getPointerCount(); i++) { - onTouchEvent( - nhandle, - event.ACTION_MOVE, - event.getPointerId(i), - event.getToolType(i), - event.getHistoricalX(i, j), - event.getHistoricalY(i, j), - scrollXScale*event.getHistoricalAxisValue(MotionEvent.AXIS_HSCROLL, i, j), - scrollYScale*event.getHistoricalAxisValue(MotionEvent.AXIS_VSCROLL, i, j), - event.getButtonState(), - time); - } - } - int act = event.getActionMasked(); - int idx = event.getActionIndex(); - for (int i = 0; i < event.getPointerCount(); i++) { - int pact = event.ACTION_MOVE; - if (i == idx) { - pact = act; - } - onTouchEvent( - nhandle, - pact, - event.getPointerId(i), - event.getToolType(i), - event.getX(i), event.getY(i), - scrollXScale*event.getAxisValue(MotionEvent.AXIS_HSCROLL, i), - scrollYScale*event.getAxisValue(MotionEvent.AXIS_VSCROLL, i), - event.getButtonState(), - event.getEventTime()); - } - } - - @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { - return new InputConnection(this); - } - - void showTextInput() { - post(new Runnable() { - @Override public void run() { - GioView.this.requestFocus(); - imm.showSoftInput(GioView.this, 0); - } - }); - } - - void hideTextInput() { - post(new Runnable() { - @Override public void run() { - imm.hideSoftInputFromWindow(getWindowToken(), 0); - } - }); - } - - @Override protected boolean fitSystemWindows(Rect insets) { - onWindowInsets(nhandle, insets.top, insets.right, insets.bottom, insets.left); - return true; - } - - void postFrameCallback() { - Choreographer.getInstance().removeFrameCallback(this); - Choreographer.getInstance().postFrameCallback(this); - } - - @Override public void doFrame(long nanos) { - onFrameCallback(nhandle, nanos); - } - - int getDensity() { - return getResources().getDisplayMetrics().densityDpi; - } - - float getFontScale() { - return getResources().getConfiguration().fontScale; - } - - void start() { - onStartView(nhandle); - } - - void stop() { - onStopView(nhandle); - } - - void destroy() { - setOnFocusChangeListener(null); - getHolder().removeCallback(surfCallbacks); - onDestroyView(nhandle); - nhandle = 0; - } - - void configurationChanged() { - onConfigurationChanged(nhandle); - } - - void lowMemory() { - onLowMemory(); - } - - boolean backPressed() { - return onBack(nhandle); - } - - static private native long onCreateView(GioView view); - static private native void onDestroyView(long handle); - static private native void onStartView(long handle); - static private native void onStopView(long handle); - static private native void onSurfaceDestroyed(long handle); - static private native void onSurfaceChanged(long handle, Surface surface); - static private native void onConfigurationChanged(long handle); - static private native void onWindowInsets(long handle, int top, int right, int bottom, int left); - static private native void onLowMemory(); - static private native void onTouchEvent(long handle, int action, int pointerID, int tool, float x, float y, float scrollX, float scrollY, int buttons, long time); - static private native void onKeyEvent(long handle, int code, int character, long time); - static private native void onFrameCallback(long handle, long nanos); - static private native boolean onBack(long handle); - static private native void onFocusChange(long handle, boolean focus); - - private static class InputConnection extends BaseInputConnection { - private final Editable editable; - - InputConnection(View view) { - // Passing false enables "dummy mode", where the BaseInputConnection - // attempts to convert IME operations to key events. - super(view, false); - editable = Editable.Factory.getInstance().newEditable(""); - } - - @Override public Editable getEditable() { - return editable; - } - } -} diff --git a/vendor/gioui.org/app/internal/window/d3d11_windows.go b/vendor/gioui.org/app/internal/window/d3d11_windows.go deleted file mode 100644 index f1e06a6..0000000 --- a/vendor/gioui.org/app/internal/window/d3d11_windows.go +++ /dev/null @@ -1,102 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package window - -import ( - "gioui.org/app/internal/d3d11" - "gioui.org/gpu/backend" -) - -type d3d11Context struct { - win *window - swchain *d3d11.SwapChain - fbo *d3d11.Framebuffer - backend backend.Device - *d3d11.Device - width, height int -} - -func init() { - backends = append(backends, gpuAPI{ - priority: 1, - initializer: func(w *window) (Context, error) { - hwnd, _, _ := w.HWND() - dev, err := d3d11.NewDevice() - if err != nil { - return nil, err - } - swchain, err := dev.CreateSwapChain(hwnd) - if err != nil { - dev.Release() - return nil, err - } - return &d3d11Context{win: w, Device: dev, swchain: swchain}, nil - }, - }) -} - -func (c *d3d11Context) Backend() (backend.Device, error) { - backend, err := d3d11.NewBackend(c.Device) - if err != nil { - return nil, err - } - c.backend = backend - c.backend.BindFramebuffer(c.fbo) - return backend, nil -} - -func (c *d3d11Context) Present() error { - if err := c.swchain.Present(); err != nil { - if err, ok := err.(d3d11.ErrorCode); ok { - switch err.Code { - case d3d11.DXGI_STATUS_OCCLUDED: - // Ignore - return nil - case d3d11.DXGI_ERROR_DEVICE_RESET, d3d11.DXGI_ERROR_DEVICE_REMOVED, d3d11.D3DDDIERR_DEVICEREMOVED: - return ErrDeviceLost - } - } - } - return nil -} - -func (c *d3d11Context) MakeCurrent() error { - _, width, height := c.win.HWND() - if c.fbo != nil && width == c.width && height == c.height { - c.backend.BindFramebuffer(c.fbo) - return nil - } - if c.fbo != nil { - c.fbo.Release() - c.fbo = nil - } - if err := c.swchain.Resize(); err != nil { - return err - } - c.width = width - c.height = height - fbo, err := c.swchain.Framebuffer(c.Device) - if err != nil { - return err - } - c.fbo = fbo - if c.backend != nil { - c.backend.BindFramebuffer(c.fbo) - } - return nil -} - -func (c *d3d11Context) Lock() {} - -func (c *d3d11Context) Unlock() {} - -func (c *d3d11Context) Release() { - if c.fbo != nil { - c.fbo.Release() - } - c.swchain.Release() - c.Device.Release() - c.fbo = nil - c.swchain = nil - c.Device = nil -} diff --git a/vendor/gioui.org/app/internal/window/egl_android.go b/vendor/gioui.org/app/internal/window/egl_android.go deleted file mode 100644 index 19c8d63..0000000 --- a/vendor/gioui.org/app/internal/window/egl_android.go +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package window - -/* -#include <EGL/egl.h> -*/ -import "C" - -import ( - "unsafe" - - "gioui.org/app/internal/egl" -) - -type context struct { - win *window - *egl.Context -} - -func (w *window) NewContext() (Context, error) { - ctx, err := egl.NewContext(nil) - if err != nil { - return nil, err - } - return &context{win: w, Context: ctx}, nil -} - -func (c *context) Release() { - if c.Context != nil { - c.Context.Release() - c.Context = nil - } -} - -func (c *context) MakeCurrent() error { - c.Context.ReleaseSurface() - win, width, height := c.win.nativeWindow(c.Context.VisualID()) - if win == nil { - return nil - } - eglSurf := egl.NativeWindowType(unsafe.Pointer(win)) - if err := c.Context.CreateSurface(eglSurf, width, height); err != nil { - return err - } - if err := c.Context.MakeCurrent(); err != nil { - return err - } - return nil -} - -func (c *context) Lock() {} - -func (c *context) Unlock() {} diff --git a/vendor/gioui.org/app/internal/window/egl_x11.go b/vendor/gioui.org/app/internal/window/egl_x11.go deleted file mode 100644 index ffe69c6..0000000 --- a/vendor/gioui.org/app/internal/window/egl_x11.go +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -// +build linux,!android,!nox11 freebsd openbsd - -package window - -import ( - "unsafe" - - "gioui.org/app/internal/egl" -) - -type x11Context struct { - win *x11Window - *egl.Context -} - -func (w *x11Window) NewContext() (Context, error) { - disp := egl.NativeDisplayType(unsafe.Pointer(w.display())) - ctx, err := egl.NewContext(disp) - if err != nil { - return nil, err - } - return &x11Context{win: w, Context: ctx}, nil -} - -func (c *x11Context) Release() { - if c.Context != nil { - c.Context.Release() - c.Context = nil - } -} - -func (c *x11Context) MakeCurrent() error { - c.Context.ReleaseSurface() - win, width, height := c.win.window() - eglSurf := egl.NativeWindowType(uintptr(win)) - if err := c.Context.CreateSurface(eglSurf, width, height); err != nil { - return err - } - if err := c.Context.MakeCurrent(); err != nil { - return err - } - c.Context.EnableVSync(true) - return nil -} - -func (c *x11Context) Lock() {} - -func (c *x11Context) Unlock() {} diff --git a/vendor/gioui.org/app/internal/window/gl_js.go b/vendor/gioui.org/app/internal/window/gl_js.go deleted file mode 100644 index 0adbdf4..0000000 --- a/vendor/gioui.org/app/internal/window/gl_js.go +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package window - -import ( - "errors" - "syscall/js" - - "gioui.org/app/internal/glimpl" - "gioui.org/app/internal/srgb" - "gioui.org/gpu/backend" - "gioui.org/gpu/gl" -) - -type context struct { - ctx js.Value - cnv js.Value - f *glimpl.Functions - srgbFBO *srgb.FBO -} - -func newContext(w *window) (*context, error) { - args := map[string]interface{}{ - // Enable low latency rendering. - // See https://developers.google.com/web/updates/2019/05/desynchronized. - "desynchronized": true, - "preserveDrawingBuffer": true, - } - version := 2 - ctx := w.cnv.Call("getContext", "webgl2", args) - if ctx.IsNull() { - version = 1 - ctx = w.cnv.Call("getContext", "webgl", args) - } - if ctx.IsNull() { - return nil, errors.New("app: webgl is not supported") - } - f := &glimpl.Functions{Ctx: ctx} - if err := f.Init(version); err != nil { - return nil, err - } - c := &context{ - ctx: ctx, - cnv: w.cnv, - f: f, - } - return c, nil -} - -func (c *context) Backend() (backend.Device, error) { - return gl.NewBackend(c.f) -} - -func (c *context) Release() { - if c.srgbFBO != nil { - c.srgbFBO.Release() - c.srgbFBO = nil - } -} - -func (c *context) Present() error { - if c.srgbFBO != nil { - c.srgbFBO.Blit() - } - if c.srgbFBO != nil { - c.srgbFBO.AfterPresent() - } - if c.ctx.Call("isContextLost").Bool() { - return errors.New("context lost") - } - return nil -} - -func (c *context) Lock() {} - -func (c *context) Unlock() {} - -func (c *context) MakeCurrent() error { - if c.srgbFBO == nil { - var err error - c.srgbFBO, err = srgb.New(c.f) - if err != nil { - c.Release() - c.srgbFBO = nil - return err - } - } - w, h := c.cnv.Get("width").Int(), c.cnv.Get("height").Int() - if err := c.srgbFBO.Refresh(w, h); err != nil { - c.Release() - return err - } - return nil -} - -func (w *window) NewContext() (Context, error) { - return newContext(w) -} diff --git a/vendor/gioui.org/app/internal/window/gl_macos.go b/vendor/gioui.org/app/internal/window/gl_macos.go deleted file mode 100644 index e4e293d..0000000 --- a/vendor/gioui.org/app/internal/window/gl_macos.go +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -// +build darwin,!ios - -package window - -import ( - "gioui.org/app/internal/glimpl" - "gioui.org/gpu/backend" - "gioui.org/gpu/gl" -) - -/* -#include <CoreFoundation/CoreFoundation.h> -#include <CoreGraphics/CoreGraphics.h> -#include <AppKit/AppKit.h> -#include <OpenGL/gl3.h> - -__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createGLView(void); -__attribute__ ((visibility ("hidden"))) CFTypeRef gio_contextForView(CFTypeRef viewRef); -__attribute__ ((visibility ("hidden"))) void gio_makeCurrentContext(CFTypeRef ctx); -__attribute__ ((visibility ("hidden"))) void gio_flushContextBuffer(CFTypeRef ctx); -__attribute__ ((visibility ("hidden"))) void gio_clearCurrentContext(void); -__attribute__ ((visibility ("hidden"))) void gio_lockContext(CFTypeRef ctxRef); -__attribute__ ((visibility ("hidden"))) void gio_unlockContext(CFTypeRef ctxRef); -*/ -import "C" - -type context struct { - c *glimpl.Functions - ctx C.CFTypeRef - view C.CFTypeRef -} - -func init() { - viewFactory = func() C.CFTypeRef { - return C.gio_createGLView() - } -} - -func newContext(w *window) (*context, error) { - view := w.contextView() - ctx := C.gio_contextForView(view) - c := &context{ - ctx: ctx, - c: new(glimpl.Functions), - view: view, - } - return c, nil -} - -func (c *context) Backend() (backend.Device, error) { - return gl.NewBackend(c.c) -} - -func (c *context) Release() { - c.Lock() - defer c.Unlock() - C.gio_clearCurrentContext() - // We could release the context with [view clearGLContext] - // and rely on [view openGLContext] auto-creating a new context. - // However that second context is not properly set up by - // OpenGLContextView, so we'll stay on the safe side and keep - // the first context around. -} - -func (c *context) Present() error { - // Assume the caller already locked the context. - C.glFlush() - return nil -} - -func (c *context) Lock() { - C.gio_lockContext(c.ctx) -} - -func (c *context) Unlock() { - C.gio_unlockContext(c.ctx) -} - -func (c *context) MakeCurrent() error { - c.Lock() - defer c.Unlock() - C.gio_makeCurrentContext(c.ctx) - return nil -} - -func (w *window) NewContext() (Context, error) { - return newContext(w) -} diff --git a/vendor/gioui.org/app/internal/window/gl_macos.m b/vendor/gioui.org/app/internal/window/gl_macos.m deleted file mode 100644 index 0d7f772..0000000 --- a/vendor/gioui.org/app/internal/window/gl_macos.m +++ /dev/null @@ -1,143 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -// +build darwin,!ios - -@import AppKit; - -#include <CoreFoundation/CoreFoundation.h> -#include <OpenGL/OpenGL.h> -#include <OpenGL/gl3.h> -#include "_cgo_export.h" - -static void handleMouse(NSView *view, NSEvent *event, int typ, CGFloat dx, CGFloat dy) { - NSPoint p = [view convertPoint:[event locationInWindow] fromView:nil]; - if (!event.hasPreciseScrollingDeltas) { - // dx and dy are in rows and columns. - dx *= 10; - dy *= 10; - } - gio_onMouse((__bridge CFTypeRef)view, typ, [NSEvent pressedMouseButtons], p.x, p.y, dx, dy, [event timestamp], [event modifierFlags]); -} - -@interface GioView : NSOpenGLView -@end - -@implementation GioView -- (instancetype)initWithFrame:(NSRect)frameRect - pixelFormat:(NSOpenGLPixelFormat *)format { - return [super initWithFrame:frameRect pixelFormat:format]; -} -- (void)prepareOpenGL { - [super prepareOpenGL]; - // Bind a default VBA to emulate OpenGL ES 2. - GLuint defVBA; - glGenVertexArrays(1, &defVBA); - glBindVertexArray(defVBA); - glEnable(GL_FRAMEBUFFER_SRGB); -} -- (BOOL)isFlipped { - return YES; -} -- (void)update { - [super update]; - [self setNeedsDisplay:YES]; -} -- (void)drawRect:(NSRect)r { - gio_onDraw((__bridge CFTypeRef)self); -} -- (void)mouseDown:(NSEvent *)event { - handleMouse(self, event, GIO_MOUSE_DOWN, 0, 0); -} -- (void)mouseUp:(NSEvent *)event { - handleMouse(self, event, GIO_MOUSE_UP, 0, 0); -} -- (void)middleMouseDown:(NSEvent *)event { - handleMouse(self, event, GIO_MOUSE_DOWN, 0, 0); -} -- (void)middletMouseUp:(NSEvent *)event { - handleMouse(self, event, GIO_MOUSE_UP, 0, 0); -} -- (void)rightMouseDown:(NSEvent *)event { - handleMouse(self, event, GIO_MOUSE_DOWN, 0, 0); -} -- (void)rightMouseUp:(NSEvent *)event { - handleMouse(self, event, GIO_MOUSE_UP, 0, 0); -} -- (void)mouseMoved:(NSEvent *)event { - handleMouse(self, event, GIO_MOUSE_MOVE, 0, 0); -} -- (void)mouseDragged:(NSEvent *)event { - handleMouse(self, event, GIO_MOUSE_MOVE, 0, 0); -} -- (void)scrollWheel:(NSEvent *)event { - CGFloat dx = -event.scrollingDeltaX; - CGFloat dy = -event.scrollingDeltaY; - handleMouse(self, event, GIO_MOUSE_SCROLL, dx, dy); -} -- (void)keyDown:(NSEvent *)event { - NSString *keys = [event charactersIgnoringModifiers]; - gio_onKeys((__bridge CFTypeRef)self, (char *)[keys UTF8String], [event timestamp], [event modifierFlags]); - [self interpretKeyEvents:[NSArray arrayWithObject:event]]; -} -- (void)insertText:(id)string { - const char *utf8 = [string UTF8String]; - gio_onText((__bridge CFTypeRef)self, (char *)utf8); -} -- (void)doCommandBySelector:(SEL)sel { - // Don't pass commands up the responder chain. - // They will end up in a beep. -} -@end - -CFTypeRef gio_createGLView(void) { - @autoreleasepool { - NSOpenGLPixelFormatAttribute attr[] = { - NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, - NSOpenGLPFAColorSize, 24, - NSOpenGLPFADepthSize, 16, - NSOpenGLPFAAccelerated, - // Opt-in to automatic GPU switching. CGL-only property. - kCGLPFASupportsAutomaticGraphicsSwitching, - NSOpenGLPFAAllowOfflineRenderers, - 0 - }; - id pixFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr]; - - NSRect frame = NSMakeRect(0, 0, 0, 0); - GioView* view = [[GioView alloc] initWithFrame:frame pixelFormat:pixFormat]; - - [view setWantsBestResolutionOpenGLSurface:YES]; - [view setWantsLayer:YES]; // The default in Mojave. - - return CFBridgingRetain(view); - } -} - -void gio_setNeedsDisplay(CFTypeRef viewRef) { - NSOpenGLView *view = (__bridge NSOpenGLView *)viewRef; - [view setNeedsDisplay:YES]; -} - -CFTypeRef gio_contextForView(CFTypeRef viewRef) { - NSOpenGLView *view = (__bridge NSOpenGLView *)viewRef; - return (__bridge CFTypeRef)view.openGLContext; -} - -void gio_clearCurrentContext(void) { - [NSOpenGLContext clearCurrentContext]; -} - -void gio_makeCurrentContext(CFTypeRef ctxRef) { - NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef; - [ctx makeCurrentContext]; -} - -void gio_lockContext(CFTypeRef ctxRef) { - NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef; - CGLLockContext([ctx CGLContextObj]); -} - -void gio_unlockContext(CFTypeRef ctxRef) { - NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef; - CGLUnlockContext([ctx CGLContextObj]); -} diff --git a/vendor/gioui.org/app/internal/window/os_android.c b/vendor/gioui.org/app/internal/window/os_android.c deleted file mode 100644 index 8a2c62d..0000000 --- a/vendor/gioui.org/app/internal/window/os_android.c +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -#include <jni.h> -#include "_cgo_export.h" - -jint gio_jni_GetEnv(JavaVM *vm, JNIEnv **env, jint version) { - return (*vm)->GetEnv(vm, (void **)env, version); -} - -jint gio_jni_GetJavaVM(JNIEnv *env, JavaVM **jvm) { - return (*env)->GetJavaVM(env, jvm); -} - -jint gio_jni_AttachCurrentThread(JavaVM *vm, JNIEnv **p_env, void *thr_args) { - return (*vm)->AttachCurrentThread(vm, p_env, thr_args); -} - -jint gio_jni_DetachCurrentThread(JavaVM *vm) { - return (*vm)->DetachCurrentThread(vm); -} - -jobject gio_jni_NewGlobalRef(JNIEnv *env, jobject obj) { - return (*env)->NewGlobalRef(env, obj); -} - -void gio_jni_DeleteGlobalRef(JNIEnv *env, jobject obj) { - (*env)->DeleteGlobalRef(env, obj); -} - -jclass gio_jni_GetObjectClass(JNIEnv *env, jobject obj) { - return (*env)->GetObjectClass(env, obj); -} - -jmethodID gio_jni_GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig) { - return (*env)->GetMethodID(env, clazz, name, sig); -} - -jmethodID gio_jni_GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig) { - return (*env)->GetStaticMethodID(env, clazz, name, sig); -} - -jfloat gio_jni_CallFloatMethod(JNIEnv *env, jobject obj, jmethodID methodID) { - return (*env)->CallFloatMethod(env, obj, methodID); -} - -jint gio_jni_CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID) { - return (*env)->CallIntMethod(env, obj, methodID); -} - -void gio_jni_CallStaticVoidMethodA(JNIEnv *env, jclass cls, jmethodID methodID, const jvalue *args) { - (*env)->CallStaticVoidMethodA(env, cls, methodID, args); -} - -void gio_jni_CallVoidMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args) { - (*env)->CallVoidMethodA(env, obj, methodID, args); -} - -jbyte *gio_jni_GetByteArrayElements(JNIEnv *env, jbyteArray arr) { - return (*env)->GetByteArrayElements(env, arr, NULL); -} - -void gio_jni_ReleaseByteArrayElements(JNIEnv *env, jbyteArray arr, jbyte *bytes) { - (*env)->ReleaseByteArrayElements(env, arr, bytes, JNI_ABORT); -} - -jsize gio_jni_GetArrayLength(JNIEnv *env, jbyteArray arr) { - return (*env)->GetArrayLength(env, arr); -} - -jstring gio_jni_NewString(JNIEnv *env, const jchar *unicodeChars, jsize len) { - return (*env)->NewString(env, unicodeChars, len); -} - -jsize gio_jni_GetStringLength(JNIEnv *env, jstring str) { - return (*env)->GetStringLength(env, str); -} - -const jchar *gio_jni_GetStringChars(JNIEnv *env, jstring str) { - return (*env)->GetStringChars(env, str, NULL); -} - -jthrowable gio_jni_ExceptionOccurred(JNIEnv *env) { - return (*env)->ExceptionOccurred(env); -} - -void gio_jni_ExceptionClear(JNIEnv *env) { - (*env)->ExceptionClear(env); -} - -jobject gio_jni_CallObjectMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args) { - return (*env)->CallObjectMethodA(env, obj, method, args); -} - -jobject gio_jni_CallStaticObjectMethodA(JNIEnv *env, jclass cls, jmethodID method, jvalue *args) { - return (*env)->CallStaticObjectMethodA(env, cls, method, args); -} diff --git a/vendor/gioui.org/app/internal/window/os_android.go b/vendor/gioui.org/app/internal/window/os_android.go deleted file mode 100644 index 3458b26..0000000 --- a/vendor/gioui.org/app/internal/window/os_android.go +++ /dev/null @@ -1,684 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package window - -/* -#cgo CFLAGS: -Werror -#cgo LDFLAGS: -landroid - -#include <android/native_window_jni.h> -#include <android/configuration.h> -#include <android/keycodes.h> -#include <android/input.h> -#include <stdlib.h> - -__attribute__ ((visibility ("hidden"))) jint gio_jni_GetEnv(JavaVM *vm, JNIEnv **env, jint version); -__attribute__ ((visibility ("hidden"))) jint gio_jni_GetJavaVM(JNIEnv *env, JavaVM **jvm); -__attribute__ ((visibility ("hidden"))) jint gio_jni_AttachCurrentThread(JavaVM *vm, JNIEnv **p_env, void *thr_args); -__attribute__ ((visibility ("hidden"))) jint gio_jni_DetachCurrentThread(JavaVM *vm); - -__attribute__ ((visibility ("hidden"))) jobject gio_jni_NewGlobalRef(JNIEnv *env, jobject obj); -__attribute__ ((visibility ("hidden"))) void gio_jni_DeleteGlobalRef(JNIEnv *env, jobject obj); -__attribute__ ((visibility ("hidden"))) jclass gio_jni_GetObjectClass(JNIEnv *env, jobject obj); -__attribute__ ((visibility ("hidden"))) jmethodID gio_jni_GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig); -__attribute__ ((visibility ("hidden"))) jmethodID gio_jni_GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig); -__attribute__ ((visibility ("hidden"))) jfloat gio_jni_CallFloatMethod(JNIEnv *env, jobject obj, jmethodID methodID); -__attribute__ ((visibility ("hidden"))) jint gio_jni_CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID); -__attribute__ ((visibility ("hidden"))) void gio_jni_CallStaticVoidMethodA(JNIEnv *env, jclass cls, jmethodID methodID, const jvalue *args); -__attribute__ ((visibility ("hidden"))) void gio_jni_CallVoidMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); -__attribute__ ((visibility ("hidden"))) jbyte *gio_jni_GetByteArrayElements(JNIEnv *env, jbyteArray arr); -__attribute__ ((visibility ("hidden"))) void gio_jni_ReleaseByteArrayElements(JNIEnv *env, jbyteArray arr, jbyte *bytes); -__attribute__ ((visibility ("hidden"))) jsize gio_jni_GetArrayLength(JNIEnv *env, jbyteArray arr); -__attribute__ ((visibility ("hidden"))) jstring gio_jni_NewString(JNIEnv *env, const jchar *unicodeChars, jsize len); -__attribute__ ((visibility ("hidden"))) jsize gio_jni_GetStringLength(JNIEnv *env, jstring str); -__attribute__ ((visibility ("hidden"))) const jchar *gio_jni_GetStringChars(JNIEnv *env, jstring str); -__attribute__ ((visibility ("hidden"))) jthrowable gio_jni_ExceptionOccurred(JNIEnv *env); -__attribute__ ((visibility ("hidden"))) void gio_jni_ExceptionClear(JNIEnv *env); -__attribute__ ((visibility ("hidden"))) jobject gio_jni_CallObjectMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args); -__attribute__ ((visibility ("hidden"))) jobject gio_jni_CallStaticObjectMethodA(JNIEnv *env, jclass cls, jmethodID method, jvalue *args); -*/ -import "C" - -import ( - "errors" - "fmt" - "image" - "reflect" - "runtime" - "runtime/debug" - "sync" - "time" - "unicode/utf16" - "unsafe" - - "gioui.org/f32" - "gioui.org/io/key" - "gioui.org/io/pointer" - "gioui.org/io/system" - "gioui.org/unit" -) - -type window struct { - callbacks Callbacks - - view C.jobject - - dpi int - fontScale float32 - insets system.Insets - - stage system.Stage - started bool - - mu sync.Mutex - win *C.ANativeWindow - animating bool - - // Cached Java methods. - mgetDensity C.jmethodID - mgetFontScale C.jmethodID - mshowTextInput C.jmethodID - mhideTextInput C.jmethodID - mpostFrameCallback C.jmethodID -} - -type jvalue uint64 // The largest JNI type fits in 64 bits. - -var dataDirChan = make(chan string, 1) - -var android struct { - // mu protects all fields of this structure. However, once a - // non-nil jvm is returned from javaVM, all the other fields may - // be accessed unlocked. - mu sync.Mutex - jvm *C.JavaVM - - // appCtx is the global Android App context. - appCtx C.jobject - // gioCls is the class of the Gio class. - gioCls C.jclass - - mwriteClipboard C.jmethodID - mreadClipboard C.jmethodID - mwakeupMainThread C.jmethodID -} - -var views = make(map[C.jlong]*window) - -var mainWindow = newWindowRendezvous() - -var mainFuncs = make(chan func(env *C.JNIEnv), 1) - -func getMethodID(env *C.JNIEnv, class C.jclass, method, sig string) C.jmethodID { - m := C.CString(method) - defer C.free(unsafe.Pointer(m)) - s := C.CString(sig) - defer C.free(unsafe.Pointer(s)) - jm := C.gio_jni_GetMethodID(env, class, m, s) - if err := exception(env); err != nil { - panic(err) - } - return jm -} - -func getStaticMethodID(env *C.JNIEnv, class C.jclass, method, sig string) C.jmethodID { - m := C.CString(method) - defer C.free(unsafe.Pointer(m)) - s := C.CString(sig) - defer C.free(unsafe.Pointer(s)) - jm := C.gio_jni_GetStaticMethodID(env, class, m, s) - if err := exception(env); err != nil { - panic(err) - } - return jm -} - -//export Java_org_gioui_Gio_runGoMain -func Java_org_gioui_Gio_runGoMain(env *C.JNIEnv, class C.jclass, jdataDir C.jbyteArray, context C.jobject) { - initJVM(env, class, context) - dirBytes := C.gio_jni_GetByteArrayElements(env, jdataDir) - if dirBytes == nil { - panic("runGoMain: GetByteArrayElements failed") - } - n := C.gio_jni_GetArrayLength(env, jdataDir) - dataDir := C.GoStringN((*C.char)(unsafe.Pointer(dirBytes)), n) - dataDirChan <- dataDir - C.gio_jni_ReleaseByteArrayElements(env, jdataDir, dirBytes) - - runMain() -} - -func initJVM(env *C.JNIEnv, gio C.jclass, ctx C.jobject) { - android.mu.Lock() - defer android.mu.Unlock() - if res := C.gio_jni_GetJavaVM(env, &android.jvm); res != 0 { - panic("gio: GetJavaVM failed") - } - android.appCtx = C.gio_jni_NewGlobalRef(env, ctx) - android.gioCls = C.jclass(C.gio_jni_NewGlobalRef(env, C.jobject(gio))) - android.mwriteClipboard = getStaticMethodID(env, gio, "writeClipboard", "(Landroid/content/Context;Ljava/lang/String;)V") - android.mreadClipboard = getStaticMethodID(env, gio, "readClipboard", "(Landroid/content/Context;)Ljava/lang/String;") - android.mwakeupMainThread = getStaticMethodID(env, gio, "wakeupMainThread", "()V") -} - -func JavaVM() uintptr { - jvm := javaVM() - return uintptr(unsafe.Pointer(jvm)) -} - -func javaVM() *C.JavaVM { - android.mu.Lock() - defer android.mu.Unlock() - return android.jvm -} - -func AppContext() uintptr { - android.mu.Lock() - defer android.mu.Unlock() - return uintptr(android.appCtx) -} - -func GetDataDir() string { - return <-dataDirChan -} - -//export Java_org_gioui_GioView_onCreateView -func Java_org_gioui_GioView_onCreateView(env *C.JNIEnv, class C.jclass, view C.jobject) C.jlong { - view = C.gio_jni_NewGlobalRef(env, view) - w := &window{ - view: view, - mgetDensity: getMethodID(env, class, "getDensity", "()I"), - mgetFontScale: getMethodID(env, class, "getFontScale", "()F"), - mshowTextInput: getMethodID(env, class, "showTextInput", "()V"), - mhideTextInput: getMethodID(env, class, "hideTextInput", "()V"), - mpostFrameCallback: getMethodID(env, class, "postFrameCallback", "()V"), - } - wopts := <-mainWindow.out - w.callbacks = wopts.window - w.callbacks.SetDriver(w) - handle := C.jlong(view) - views[handle] = w - w.loadConfig(env, class) - w.setStage(system.StagePaused) - return handle -} - -//export Java_org_gioui_GioView_onDestroyView -func Java_org_gioui_GioView_onDestroyView(env *C.JNIEnv, class C.jclass, handle C.jlong) { - w := views[handle] - w.callbacks.SetDriver(nil) - delete(views, handle) - C.gio_jni_DeleteGlobalRef(env, w.view) - w.view = 0 -} - -//export Java_org_gioui_GioView_onStopView -func Java_org_gioui_GioView_onStopView(env *C.JNIEnv, class C.jclass, handle C.jlong) { - w := views[handle] - w.started = false - w.setStage(system.StagePaused) -} - -//export Java_org_gioui_GioView_onStartView -func Java_org_gioui_GioView_onStartView(env *C.JNIEnv, class C.jclass, handle C.jlong) { - w := views[handle] - w.started = true - if w.aNativeWindow() != nil { - w.setVisible() - } -} - -//export Java_org_gioui_GioView_onSurfaceDestroyed -func Java_org_gioui_GioView_onSurfaceDestroyed(env *C.JNIEnv, class C.jclass, handle C.jlong) { - w := views[handle] - w.mu.Lock() - w.win = nil - w.mu.Unlock() - w.setStage(system.StagePaused) -} - -//export Java_org_gioui_GioView_onSurfaceChanged -func Java_org_gioui_GioView_onSurfaceChanged(env *C.JNIEnv, class C.jclass, handle C.jlong, surf C.jobject) { - w := views[handle] - w.mu.Lock() - w.win = C.ANativeWindow_fromSurface(env, surf) - w.mu.Unlock() - if w.started { - w.setVisible() - } -} - -//export Java_org_gioui_GioView_onLowMemory -func Java_org_gioui_GioView_onLowMemory() { - runtime.GC() - debug.FreeOSMemory() -} - -//export Java_org_gioui_GioView_onConfigurationChanged -func Java_org_gioui_GioView_onConfigurationChanged(env *C.JNIEnv, class C.jclass, view C.jlong) { - w := views[view] - w.loadConfig(env, class) - if w.stage >= system.StageRunning { - w.draw(true) - } -} - -//export Java_org_gioui_GioView_onFrameCallback -func Java_org_gioui_GioView_onFrameCallback(env *C.JNIEnv, class C.jclass, view C.jlong, nanos C.jlong) { - w, exist := views[view] - if !exist { - return - } - if w.stage < system.StageRunning { - return - } - w.mu.Lock() - anim := w.animating - w.mu.Unlock() - if anim { - runInJVM(javaVM(), func(env *C.JNIEnv) { - callVoidMethod(env, w.view, w.mpostFrameCallback) - }) - w.draw(false) - } -} - -//export Java_org_gioui_GioView_onBack -func Java_org_gioui_GioView_onBack(env *C.JNIEnv, class C.jclass, view C.jlong) C.jboolean { - w := views[view] - ev := &system.CommandEvent{Type: system.CommandBack} - w.callbacks.Event(ev) - if ev.Cancel { - return C.JNI_TRUE - } - return C.JNI_FALSE -} - -//export Java_org_gioui_GioView_onFocusChange -func Java_org_gioui_GioView_onFocusChange(env *C.JNIEnv, class C.jclass, view C.jlong, focus C.jboolean) { - w := views[view] - w.callbacks.Event(key.FocusEvent{Focus: focus == C.JNI_TRUE}) -} - -//export Java_org_gioui_GioView_onWindowInsets -func Java_org_gioui_GioView_onWindowInsets(env *C.JNIEnv, class C.jclass, view C.jlong, top, right, bottom, left C.jint) { - w := views[view] - w.insets = system.Insets{ - Top: unit.Px(float32(top)), - Right: unit.Px(float32(right)), - Bottom: unit.Px(float32(bottom)), - Left: unit.Px(float32(left)), - } - if w.stage >= system.StageRunning { - w.draw(true) - } -} - -func (w *window) setVisible() { - win := w.aNativeWindow() - width, height := C.ANativeWindow_getWidth(win), C.ANativeWindow_getHeight(win) - if width == 0 || height == 0 { - return - } - w.setStage(system.StageRunning) - w.draw(true) -} - -func (w *window) setStage(stage system.Stage) { - if stage == w.stage { - return - } - w.stage = stage - w.callbacks.Event(system.StageEvent{stage}) -} - -func (w *window) nativeWindow(visID int) (*C.ANativeWindow, int, int) { - win := w.aNativeWindow() - var width, height int - if win != nil { - if C.ANativeWindow_setBuffersGeometry(win, 0, 0, C.int32_t(visID)) != 0 { - panic(errors.New("ANativeWindow_setBuffersGeometry failed")) - } - w, h := C.ANativeWindow_getWidth(win), C.ANativeWindow_getHeight(win) - width, height = int(w), int(h) - } - return win, width, height -} - -func (w *window) aNativeWindow() *C.ANativeWindow { - w.mu.Lock() - defer w.mu.Unlock() - return w.win -} - -func (w *window) loadConfig(env *C.JNIEnv, class C.jclass) { - dpi := int(C.gio_jni_CallIntMethod(env, w.view, w.mgetDensity)) - w.fontScale = float32(C.gio_jni_CallFloatMethod(env, w.view, w.mgetFontScale)) - switch dpi { - case C.ACONFIGURATION_DENSITY_NONE, - C.ACONFIGURATION_DENSITY_DEFAULT, - C.ACONFIGURATION_DENSITY_ANY: - // Assume standard density. - w.dpi = C.ACONFIGURATION_DENSITY_MEDIUM - default: - w.dpi = int(dpi) - } -} - -func (w *window) SetAnimating(anim bool) { - w.mu.Lock() - w.animating = anim - w.mu.Unlock() - if anim { - runOnMain(func(env *C.JNIEnv) { - if w.view == 0 { - // View was destroyed while switching to main thread. - return - } - callVoidMethod(env, w.view, w.mpostFrameCallback) - }) - } -} - -func (w *window) draw(sync bool) { - win := w.aNativeWindow() - width, height := C.ANativeWindow_getWidth(win), C.ANativeWindow_getHeight(win) - if width == 0 || height == 0 { - return - } - const inchPrDp = 1.0 / 160 - ppdp := float32(w.dpi) * inchPrDp - w.callbacks.Event(FrameEvent{ - FrameEvent: system.FrameEvent{ - Now: time.Now(), - Size: image.Point{ - X: int(width), - Y: int(height), - }, - Insets: w.insets, - Metric: unit.Metric{ - PxPerDp: ppdp, - PxPerSp: w.fontScale * ppdp, - }, - }, - Sync: sync, - }) -} - -type keyMapper func(devId, keyCode C.int32_t) rune - -func runInJVM(jvm *C.JavaVM, f func(env *C.JNIEnv)) { - if jvm == nil { - panic("nil JVM") - } - runtime.LockOSThread() - defer runtime.UnlockOSThread() - var env *C.JNIEnv - if res := C.gio_jni_GetEnv(jvm, &env, C.JNI_VERSION_1_6); res != C.JNI_OK { - if res != C.JNI_EDETACHED { - panic(fmt.Errorf("JNI GetEnv failed with error %d", res)) - } - if C.gio_jni_AttachCurrentThread(jvm, &env, nil) != C.JNI_OK { - panic(errors.New("runInJVM: AttachCurrentThread failed")) - } - defer C.gio_jni_DetachCurrentThread(jvm) - } - - f(env) -} - -func convertKeyCode(code C.jint) (string, bool) { - var n string - switch code { - case C.AKEYCODE_DPAD_UP: - n = key.NameUpArrow - case C.AKEYCODE_DPAD_DOWN: - n = key.NameDownArrow - case C.AKEYCODE_DPAD_LEFT: - n = key.NameLeftArrow - case C.AKEYCODE_DPAD_RIGHT: - n = key.NameRightArrow - case C.AKEYCODE_FORWARD_DEL: - n = key.NameDeleteForward - case C.AKEYCODE_DEL: - n = key.NameDeleteBackward - default: - return "", false - } - return n, true -} - -//export Java_org_gioui_GioView_onKeyEvent -func Java_org_gioui_GioView_onKeyEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, keyCode, r C.jint, t C.jlong) { - w := views[handle] - if n, ok := convertKeyCode(keyCode); ok { - w.callbacks.Event(key.Event{Name: n}) - } - if r != 0 { - w.callbacks.Event(key.EditEvent{Text: string(rune(r))}) - } -} - -//export Java_org_gioui_GioView_onTouchEvent -func Java_org_gioui_GioView_onTouchEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, action, pointerID, tool C.jint, x, y, scrollX, scrollY C.jfloat, jbtns C.jint, t C.jlong) { - w := views[handle] - var typ pointer.Type - switch action { - case C.AMOTION_EVENT_ACTION_DOWN, C.AMOTION_EVENT_ACTION_POINTER_DOWN: - typ = pointer.Press - case C.AMOTION_EVENT_ACTION_UP, C.AMOTION_EVENT_ACTION_POINTER_UP: - typ = pointer.Release - case C.AMOTION_EVENT_ACTION_CANCEL: - typ = pointer.Cancel - case C.AMOTION_EVENT_ACTION_MOVE: - typ = pointer.Move - case C.AMOTION_EVENT_ACTION_SCROLL: - typ = pointer.Scroll - default: - return - } - var src pointer.Source - var btns pointer.Buttons - if jbtns&C.AMOTION_EVENT_BUTTON_PRIMARY != 0 { - btns |= pointer.ButtonLeft - } - if jbtns&C.AMOTION_EVENT_BUTTON_SECONDARY != 0 { - btns |= pointer.ButtonRight - } - if jbtns&C.AMOTION_EVENT_BUTTON_TERTIARY != 0 { - btns |= pointer.ButtonMiddle - } - switch tool { - case C.AMOTION_EVENT_TOOL_TYPE_FINGER: - src = pointer.Touch - case C.AMOTION_EVENT_TOOL_TYPE_MOUSE: - src = pointer.Mouse - case C.AMOTION_EVENT_TOOL_TYPE_UNKNOWN: - // For example, triggered via 'adb shell input tap'. - // Instead of discarding it, treat it as a touch event. - src = pointer.Touch - default: - return - } - w.callbacks.Event(pointer.Event{ - Type: typ, - Source: src, - Buttons: btns, - PointerID: pointer.ID(pointerID), - Time: time.Duration(t) * time.Millisecond, - Position: f32.Point{X: float32(x), Y: float32(y)}, - Scroll: f32.Pt(float32(scrollX), float32(scrollY)), - }) -} - -func (w *window) ShowTextInput(show bool) { - if w.view == 0 { - return - } - runInJVM(javaVM(), func(env *C.JNIEnv) { - if show { - callVoidMethod(env, w.view, w.mshowTextInput) - } else { - callVoidMethod(env, w.view, w.mhideTextInput) - } - }) -} - -func javaString(env *C.JNIEnv, str string) C.jstring { - if str == "" { - return 0 - } - utf16Chars := utf16.Encode([]rune(str)) - return C.gio_jni_NewString(env, (*C.jchar)(unsafe.Pointer(&utf16Chars[0])), C.int(len(utf16Chars))) -} - -// Do invokes the function with a global JNI handle to the view. If -// the view is destroyed, Do returns false and does not invoke the -// function. -// -// NOTE: Do must be invoked on the Android main thread. -func (w *window) Do(f func(view uintptr)) bool { - if w.view == 0 { - return false - } - runInJVM(javaVM(), func(env *C.JNIEnv) { - view := C.gio_jni_NewGlobalRef(env, w.view) - defer C.gio_jni_DeleteGlobalRef(env, view) - f(uintptr(view)) - }) - return true -} - -func varArgs(args []jvalue) *C.jvalue { - if len(args) == 0 { - return nil - } - return (*C.jvalue)(unsafe.Pointer(&args[0])) -} - -func callStaticVoidMethod(env *C.JNIEnv, cls C.jclass, method C.jmethodID, args ...jvalue) error { - C.gio_jni_CallStaticVoidMethodA(env, cls, method, varArgs(args)) - return exception(env) -} - -func callStaticObjectMethod(env *C.JNIEnv, cls C.jclass, method C.jmethodID, args ...jvalue) (C.jobject, error) { - res := C.gio_jni_CallStaticObjectMethodA(env, cls, method, varArgs(args)) - return res, exception(env) -} - -func callVoidMethod(env *C.JNIEnv, obj C.jobject, method C.jmethodID, args ...jvalue) error { - C.gio_jni_CallVoidMethodA(env, obj, method, varArgs(args)) - return exception(env) -} - -func callObjectMethod(env *C.JNIEnv, obj C.jobject, method C.jmethodID, args ...jvalue) (C.jobject, error) { - res := C.gio_jni_CallObjectMethodA(env, obj, method, varArgs(args)) - return res, exception(env) -} - -// exception returns an error corresponding to the pending -// exception, or nil if no exception is pending. The pending -// exception is cleared. -func exception(env *C.JNIEnv) error { - thr := C.gio_jni_ExceptionOccurred(env) - if thr == 0 { - return nil - } - C.gio_jni_ExceptionClear(env) - cls := getObjectClass(env, C.jobject(thr)) - toString := getMethodID(env, cls, "toString", "()Ljava/lang/String;") - msg, err := callObjectMethod(env, C.jobject(thr), toString) - if err != nil { - return err - } - return errors.New(goString(env, C.jstring(msg))) -} - -func getObjectClass(env *C.JNIEnv, obj C.jobject) C.jclass { - if obj == 0 { - panic("null object") - } - cls := C.gio_jni_GetObjectClass(env, C.jobject(obj)) - if err := exception(env); err != nil { - // GetObjectClass should never fail. - panic(err) - } - return cls -} - -// goString converts the JVM jstring to a Go string. -func goString(env *C.JNIEnv, str C.jstring) string { - if str == 0 { - return "" - } - strlen := C.gio_jni_GetStringLength(env, C.jstring(str)) - chars := C.gio_jni_GetStringChars(env, C.jstring(str)) - var utf16Chars []uint16 - hdr := (*reflect.SliceHeader)(unsafe.Pointer(&utf16Chars)) - hdr.Data = uintptr(unsafe.Pointer(chars)) - hdr.Cap = int(strlen) - hdr.Len = int(strlen) - utf8 := utf16.Decode(utf16Chars) - return string(utf8) -} - -func Main() { -} - -func NewWindow(window Callbacks, opts *Options) error { - mainWindow.in <- windowAndOptions{window, opts} - return <-mainWindow.errs -} - -func (w *window) WriteClipboard(s string) { - runOnMain(func(env *C.JNIEnv) { - jstr := javaString(env, s) - callStaticVoidMethod(env, android.gioCls, android.mwriteClipboard, - jvalue(android.appCtx), jvalue(jstr)) - }) -} - -func (w *window) ReadClipboard() { - runOnMain(func(env *C.JNIEnv) { - c, err := callStaticObjectMethod(env, android.gioCls, android.mreadClipboard, - jvalue(android.appCtx)) - if err != nil { - return - } - content := goString(env, C.jstring(c)) - w.callbacks.Event(system.ClipboardEvent{Text: content}) - }) -} - -// Close the window. Not implemented for Android. -func (w *window) Close() {} - -// RunOnMain is the exported version of runOnMain without a JNI -// environement. -func RunOnMain(f func()) { - runOnMain(func(_ *C.JNIEnv) { - f() - }) -} - -// runOnMain runs a function on the Java main thread. -func runOnMain(f func(env *C.JNIEnv)) { - go func() { - mainFuncs <- f - runInJVM(javaVM(), func(env *C.JNIEnv) { - callStaticVoidMethod(env, android.gioCls, android.mwakeupMainThread) - }) - }() -} - -//export Java_org_gioui_Gio_scheduleMainFuncs -func Java_org_gioui_Gio_scheduleMainFuncs(env *C.JNIEnv, cls C.jclass) { - for { - select { - case f := <-mainFuncs: - f(env) - default: - return - } - } -} diff --git a/vendor/gioui.org/app/internal/window/os_darwin.m b/vendor/gioui.org/app/internal/window/os_darwin.m deleted file mode 100644 index dc4f00b..0000000 --- a/vendor/gioui.org/app/internal/window/os_darwin.m +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -@import Dispatch; -@import Foundation; - -#include "_cgo_export.h" - -void gio_wakeupMainThread(void) { - dispatch_async(dispatch_get_main_queue(), ^{ - gio_dispatchMainFuncs(); - }); -} - -NSUInteger gio_nsstringLength(CFTypeRef cstr) { - NSString *str = (__bridge NSString *)cstr; - return [str length]; -} - -void gio_nsstringGetCharacters(CFTypeRef cstr, unichar *chars, NSUInteger loc, NSUInteger length) { - NSString *str = (__bridge NSString *)cstr; - [str getCharacters:chars range:NSMakeRange(loc, length)]; -} diff --git a/vendor/gioui.org/app/internal/window/os_macos.go b/vendor/gioui.org/app/internal/window/os_macos.go deleted file mode 100644 index ef95b70..0000000 --- a/vendor/gioui.org/app/internal/window/os_macos.go +++ /dev/null @@ -1,460 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -// +build darwin,!ios - -package window - -import ( - "errors" - "image" - "runtime" - "time" - "unicode" - "unicode/utf16" - "unsafe" - - "gioui.org/f32" - "gioui.org/io/key" - "gioui.org/io/pointer" - "gioui.org/io/system" - "gioui.org/unit" - - _ "gioui.org/app/internal/cocoainit" -) - -/* -#cgo CFLAGS: -DGL_SILENCE_DEPRECATION -Werror -Wno-deprecated-declarations -fmodules -fobjc-arc -x objective-c - -#include <AppKit/AppKit.h> - -#define GIO_MOUSE_MOVE 1 -#define GIO_MOUSE_UP 2 -#define GIO_MOUSE_DOWN 3 -#define GIO_MOUSE_SCROLL 4 - -__attribute__ ((visibility ("hidden"))) void gio_main(void); -__attribute__ ((visibility ("hidden"))) CGFloat gio_viewWidth(CFTypeRef viewRef); -__attribute__ ((visibility ("hidden"))) CGFloat gio_viewHeight(CFTypeRef viewRef); -__attribute__ ((visibility ("hidden"))) CGFloat gio_getViewBackingScale(CFTypeRef viewRef); -__attribute__ ((visibility ("hidden"))) CGFloat gio_getScreenBackingScale(void); -__attribute__ ((visibility ("hidden"))) CFTypeRef gio_readClipboard(void); -__attribute__ ((visibility ("hidden"))) void gio_writeClipboard(unichar *chars, NSUInteger length); -__attribute__ ((visibility ("hidden"))) void gio_setNeedsDisplay(CFTypeRef viewRef); -__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight); -__attribute__ ((visibility ("hidden"))) void gio_makeKeyAndOrderFront(CFTypeRef windowRef); -__attribute__ ((visibility ("hidden"))) NSPoint gio_cascadeTopLeftFromPoint(CFTypeRef windowRef, NSPoint topLeft); -__attribute__ ((visibility ("hidden"))) void gio_close(CFTypeRef windowRef); -*/ -import "C" - -func init() { - // Darwin requires that UI operations happen on the main thread only. - runtime.LockOSThread() -} - -type window struct { - view C.CFTypeRef - window C.CFTypeRef - w Callbacks - stage system.Stage - displayLink *displayLink - - scale float32 -} - -// viewMap is the mapping from Cocoa NSViews to Go windows. -var viewMap = make(map[C.CFTypeRef]*window) - -var viewFactory func() C.CFTypeRef - -// launched is closed when applicationDidFinishLaunching is called. -var launched = make(chan struct{}) - -// nextTopLeft is the offset to use for the next window's call to -// cascadeTopLeftFromPoint. -var nextTopLeft C.NSPoint - -// mustView is like lookoupView, except that it panics -// if the view isn't mapped. -func mustView(view C.CFTypeRef) *window { - w, ok := lookupView(view) - if !ok { - panic("no window for view") - } - return w -} - -func lookupView(view C.CFTypeRef) (*window, bool) { - w, exists := viewMap[view] - if !exists { - return nil, false - } - return w, true -} - -func deleteView(view C.CFTypeRef) { - delete(viewMap, view) -} - -func insertView(view C.CFTypeRef, w *window) { - viewMap[view] = w -} - -func (w *window) contextView() C.CFTypeRef { - return w.view -} - -func (w *window) ReadClipboard() { - runOnMain(func() { - content := nsstringToString(C.gio_readClipboard()) - w.w.Event(system.ClipboardEvent{Text: content}) - }) -} - -func (w *window) WriteClipboard(s string) { - u16 := utf16.Encode([]rune(s)) - runOnMain(func() { - var chars *C.unichar - if len(u16) > 0 { - chars = (*C.unichar)(unsafe.Pointer(&u16[0])) - } - C.gio_writeClipboard(chars, C.NSUInteger(len(u16))) - }) -} - -func (w *window) ShowTextInput(show bool) {} - -func (w *window) SetAnimating(anim bool) { - if anim { - w.displayLink.Start() - } else { - w.displayLink.Stop() - } -} - -func (w *window) Close() { - runOnMain(func() { - // Make sure the view is still valid. The window might've been closed - // during the switch to the main thread. - if w.view != 0 { - C.gio_close(w.window) - } - }) -} - -func (w *window) setStage(stage system.Stage) { - if stage == w.stage { - return - } - w.stage = stage - w.w.Event(system.StageEvent{Stage: stage}) -} - -//export gio_onKeys -func gio_onKeys(view C.CFTypeRef, cstr *C.char, ti C.double, mods C.NSUInteger) { - str := C.GoString(cstr) - kmods := convertMods(mods) - w := mustView(view) - for _, k := range str { - if n, ok := convertKey(k); ok { - w.w.Event(key.Event{ - Name: n, - Modifiers: kmods, - }) - } - } -} - -//export gio_onText -func gio_onText(view C.CFTypeRef, cstr *C.char) { - str := C.GoString(cstr) - w := mustView(view) - w.w.Event(key.EditEvent{Text: str}) -} - -//export gio_onMouse -func gio_onMouse(view C.CFTypeRef, cdir C.int, cbtns C.NSUInteger, x, y, dx, dy C.CGFloat, ti C.double, mods C.NSUInteger) { - var typ pointer.Type - switch cdir { - case C.GIO_MOUSE_MOVE: - typ = pointer.Move - case C.GIO_MOUSE_UP: - typ = pointer.Release - case C.GIO_MOUSE_DOWN: - typ = pointer.Press - case C.GIO_MOUSE_SCROLL: - typ = pointer.Scroll - default: - panic("invalid direction") - } - var btns pointer.Buttons - if cbtns&(1<<0) != 0 { - btns |= pointer.ButtonLeft - } - if cbtns&(1<<1) != 0 { - btns |= pointer.ButtonRight - } - if cbtns&(1<<2) != 0 { - btns |= pointer.ButtonMiddle - } - t := time.Duration(float64(ti)*float64(time.Second) + .5) - w := mustView(view) - xf, yf := float32(x)*w.scale, float32(y)*w.scale - dxf, dyf := float32(dx)*w.scale, float32(dy)*w.scale - w.w.Event(pointer.Event{ - Type: typ, - Source: pointer.Mouse, - Time: t, - Buttons: btns, - Position: f32.Point{X: xf, Y: yf}, - Scroll: f32.Point{X: dxf, Y: dyf}, - Modifiers: convertMods(mods), - }) -} - -//export gio_onDraw -func gio_onDraw(view C.CFTypeRef) { - w := mustView(view) - w.draw() -} - -//export gio_onFocus -func gio_onFocus(view C.CFTypeRef, focus C.BOOL) { - w := mustView(view) - w.w.Event(key.FocusEvent{Focus: focus == C.YES}) -} - -//export gio_onChangeScreen -func gio_onChangeScreen(view C.CFTypeRef, did uint64) { - w := mustView(view) - w.displayLink.SetDisplayID(did) -} - -func (w *window) draw() { - w.scale = float32(C.gio_getViewBackingScale(w.view)) - wf, hf := float32(C.gio_viewWidth(w.view)), float32(C.gio_viewHeight(w.view)) - if wf == 0 || hf == 0 { - return - } - width := int(wf*w.scale + .5) - height := int(hf*w.scale + .5) - cfg := configFor(w.scale) - w.setStage(system.StageRunning) - w.w.Event(FrameEvent{ - FrameEvent: system.FrameEvent{ - Now: time.Now(), - Size: image.Point{ - X: width, - Y: height, - }, - Metric: cfg, - }, - Sync: true, - }) -} - -func configFor(scale float32) unit.Metric { - return unit.Metric{ - PxPerDp: scale, - PxPerSp: scale, - } -} - -//export gio_onClose -func gio_onClose(view C.CFTypeRef) { - w := mustView(view) - w.displayLink.Close() - deleteView(view) - w.w.Event(system.DestroyEvent{}) - C.CFRelease(w.view) - w.view = 0 - C.CFRelease(w.window) - w.window = 0 -} - -//export gio_onHide -func gio_onHide(view C.CFTypeRef) { - w := mustView(view) - w.setStage(system.StagePaused) -} - -//export gio_onShow -func gio_onShow(view C.CFTypeRef) { - w := mustView(view) - w.setStage(system.StageRunning) -} - -//export gio_onAppHide -func gio_onAppHide() { - for _, w := range viewMap { - w.setStage(system.StagePaused) - } -} - -//export gio_onAppShow -func gio_onAppShow() { - for _, w := range viewMap { - w.setStage(system.StageRunning) - } -} - -//export gio_onFinishLaunching -func gio_onFinishLaunching() { - close(launched) -} - -func NewWindow(win Callbacks, opts *Options) error { - <-launched - errch := make(chan error) - runOnMain(func() { - w, err := newWindow(opts) - if err != nil { - errch <- err - return - } - screenScale := float32(C.gio_getScreenBackingScale()) - cfg := configFor(screenScale) - width := cfg.Px(opts.Width) - height := cfg.Px(opts.Height) - // Window sizes is in unscaled screen coordinates, not device pixels. - width = int(float32(width) / screenScale) - height = int(float32(height) / screenScale) - minWidth := cfg.Px(opts.MinWidth) - minHeight := cfg.Px(opts.MinHeight) - minWidth = int(float32(minWidth) / screenScale) - minHeight = int(float32(minHeight) / screenScale) - maxWidth := cfg.Px(opts.MaxWidth) - maxHeight := cfg.Px(opts.MaxHeight) - maxWidth = int(float32(maxWidth) / screenScale) - maxHeight = int(float32(maxHeight) / screenScale) - title := C.CString(opts.Title) - defer C.free(unsafe.Pointer(title)) - errch <- nil - win.SetDriver(w) - w.w = win - w.window = C.gio_createWindow(w.view, title, C.CGFloat(width), C.CGFloat(height), - C.CGFloat(minWidth), C.CGFloat(minHeight), C.CGFloat(maxWidth), C.CGFloat(maxHeight)) - if nextTopLeft.x == 0 && nextTopLeft.y == 0 { - // cascadeTopLeftFromPoint treats (0, 0) as a no-op, - // and just returns the offset we need for the first window. - nextTopLeft = C.gio_cascadeTopLeftFromPoint(w.window, nextTopLeft) - } - nextTopLeft = C.gio_cascadeTopLeftFromPoint(w.window, nextTopLeft) - C.gio_makeKeyAndOrderFront(w.window) - }) - return <-errch -} - -func newWindow(opts *Options) (*window, error) { - view := viewFactory() - if view == 0 { - return nil, errors.New("CreateWindow: failed to create view") - } - scale := float32(C.gio_getViewBackingScale(view)) - w := &window{ - view: view, - scale: scale, - } - dl, err := NewDisplayLink(func() { - runOnMain(func() { - if w.view != 0 { - C.gio_setNeedsDisplay(w.view) - } - }) - }) - w.displayLink = dl - if err != nil { - C.CFRelease(view) - return nil, err - } - insertView(view, w) - return w, nil -} - -func Main() { - C.gio_main() -} - -func convertKey(k rune) (string, bool) { - var n string - switch k { - case 0x1b: - n = key.NameEscape - case C.NSLeftArrowFunctionKey: - n = key.NameLeftArrow - case C.NSRightArrowFunctionKey: - n = key.NameRightArrow - case C.NSUpArrowFunctionKey: - n = key.NameUpArrow - case C.NSDownArrowFunctionKey: - n = key.NameDownArrow - case 0xd: - n = key.NameReturn - case 0x3: - n = key.NameEnter - case C.NSHomeFunctionKey: - n = key.NameHome - case C.NSEndFunctionKey: - n = key.NameEnd - case 0x7f: - n = key.NameDeleteBackward - case C.NSDeleteFunctionKey: - n = key.NameDeleteForward - case C.NSPageUpFunctionKey: - n = key.NamePageUp - case C.NSPageDownFunctionKey: - n = key.NamePageDown - case C.NSF1FunctionKey: - n = "F1" - case C.NSF2FunctionKey: - n = "F2" - case C.NSF3FunctionKey: - n = "F3" - case C.NSF4FunctionKey: - n = "F4" - case C.NSF5FunctionKey: - n = "F5" - case C.NSF6FunctionKey: - n = "F6" - case C.NSF7FunctionKey: - n = "F7" - case C.NSF8FunctionKey: - n = "F8" - case C.NSF9FunctionKey: - n = "F9" - case C.NSF10FunctionKey: - n = "F10" - case C.NSF11FunctionKey: - n = "F11" - case C.NSF12FunctionKey: - n = "F12" - case 0x09, 0x19: - n = key.NameTab - case 0x20: - n = "Space" - default: - k = unicode.ToUpper(k) - if !unicode.IsPrint(k) { - return "", false - } - n = string(k) - } - return n, true -} - -func convertMods(mods C.NSUInteger) key.Modifiers { - var kmods key.Modifiers - if mods&C.NSAlternateKeyMask != 0 { - kmods |= key.ModAlt - } - if mods&C.NSControlKeyMask != 0 { - kmods |= key.ModCtrl - } - if mods&C.NSCommandKeyMask != 0 { - kmods |= key.ModCommand - } - if mods&C.NSShiftKeyMask != 0 { - kmods |= key.ModShift - } - return kmods -} diff --git a/vendor/gioui.org/app/internal/window/os_unix.go b/vendor/gioui.org/app/internal/window/os_unix.go deleted file mode 100644 index baaebb5..0000000 --- a/vendor/gioui.org/app/internal/window/os_unix.go +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -// +build linux,!android freebsd openbsd - -package window - -import ( - "errors" -) - -func Main() { - select {} -} - -// instead of creating files with build tags for each combination of wayland +/- x11 -// let each driver initialize these variables with their own version of createWindow. -var wlDriver, x11Driver func(Callbacks, *Options) error - -func NewWindow(window Callbacks, opts *Options) error { - var errFirst, err error - if wlDriver != nil { - if err = wlDriver(window, opts); err == nil { - return nil - } - errFirst = err - } - if x11Driver != nil { - if err = x11Driver(window, opts); err == nil { - return nil - } - if errFirst == nil { - errFirst = err - } - } - if errFirst != nil { - return errFirst - } - return errors.New("app: no window driver available") -} diff --git a/vendor/gioui.org/app/internal/window/window.go b/vendor/gioui.org/app/internal/window/window.go deleted file mode 100644 index 30ba360..0000000 --- a/vendor/gioui.org/app/internal/window/window.go +++ /dev/null @@ -1,102 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -// Package window implements platform specific windows -// and GPU contexts. -package window - -import ( - "errors" - - "gioui.org/gpu/backend" - "gioui.org/io/event" - "gioui.org/io/system" - "gioui.org/unit" -) - -type Options struct { - Width, Height unit.Value - MinWidth, MinHeight unit.Value - MaxWidth, MaxHeight unit.Value - Title string -} - -type FrameEvent struct { - system.FrameEvent - - Sync bool -} - -type Callbacks interface { - SetDriver(d Driver) - Event(e event.Event) -} - -type Context interface { - Backend() (backend.Device, error) - Present() error - MakeCurrent() error - Release() - Lock() - Unlock() -} - -// ErrDeviceLost is returned from Context.Present when -// the underlying GPU device is gone and should be -// recreated. -var ErrDeviceLost = errors.New("GPU device lost") - -// Driver is the interface for the platform implementation -// of a window. -type Driver interface { - // SetAnimating sets the animation flag. When the window is animating, - // FrameEvents are delivered as fast as the display can handle them. - SetAnimating(anim bool) - // ShowTextInput updates the virtual keyboard state. - ShowTextInput(show bool) - NewContext() (Context, error) - - // ReadClipboard requests the clipboard content. - ReadClipboard() - // WriteClipboard requests a clipboard write. - WriteClipboard(s string) - - // Close the window. - Close() -} - -type windowRendezvous struct { - in chan windowAndOptions - out chan windowAndOptions - errs chan error -} - -type windowAndOptions struct { - window Callbacks - opts *Options -} - -func newWindowRendezvous() *windowRendezvous { - wr := &windowRendezvous{ - in: make(chan windowAndOptions), - out: make(chan windowAndOptions), - errs: make(chan error), - } - go func() { - var main windowAndOptions - var out chan windowAndOptions - for { - select { - case w := <-wr.in: - var err error - if main.window != nil { - err = errors.New("multiple windows are not supported") - } - wr.errs <- err - main = w - out = wr.out - case out <- main: - } - } - }() - return wr -} diff --git a/vendor/gioui.org/app/internal/windows/windows.go b/vendor/gioui.org/app/internal/windows/windows.go index 9006326..65a01d3 100644 --- a/vendor/gioui.org/app/internal/windows/windows.go +++ b/vendor/gioui.org/app/internal/windows/windows.go @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Unlicense OR MIT +//go:build windows // +build windows package windows @@ -54,14 +55,44 @@ type MinMaxInfo struct { PtMaxTrackSize Point } +type WindowPlacement struct { + length uint32 + flags uint32 + showCmd uint32 + ptMinPosition Point + ptMaxPosition Point + rcNormalPosition Rect + rcDevice Rect +} + +type MonitorInfo struct { + cbSize uint32 + Monitor Rect + WorkArea Rect + Flags uint32 +} + const ( + TRUE = 1 + CS_HREDRAW = 0x0002 CS_VREDRAW = 0x0001 CS_OWNDC = 0x0020 CW_USEDEFAULT = -2147483648 - IDC_ARROW = 32512 + GWL_STYLE = ^(uint32(16) - 1) // -16 + HWND_TOPMOST = ^(uint32(1) - 1) // -1 + + HTCLIENT = 1 + + IDC_ARROW = 32512 + IDC_IBEAM = 32513 + IDC_HAND = 32649 + IDC_CROSS = 32515 + IDC_SIZENS = 32645 + IDC_SIZEWE = 32644 + IDC_SIZEALL = 32646 INFINITE = 0xFFFFFFFF @@ -77,6 +108,13 @@ const ( SW_SHOWDEFAULT = 10 + SWP_FRAMECHANGED = 0x0020 + SWP_NOMOVE = 0x0002 + SWP_NOOWNERZORDER = 0x0200 + SWP_NOSIZE = 0x0001 + SWP_NOZORDER = 0x0004 + SWP_SHOWWINDOW = 0x0040 + USER_TIMER_MINIMUM = 0x0000000A VK_CONTROL = 0x11 @@ -142,14 +180,17 @@ const ( WM_MBUTTONUP = 0x0208 WM_MOUSEMOVE = 0x0200 WM_MOUSEWHEEL = 0x020A + WM_MOUSEHWHEEL = 0x020E WM_PAINT = 0x000F WM_CLOSE = 0x0010 WM_QUIT = 0x0012 + WM_SETCURSOR = 0x0020 WM_SETFOCUS = 0x0007 WM_KILLFOCUS = 0x0008 WM_SHOWWINDOW = 0x0018 WM_SIZE = 0x0005 WM_SYSKEYDOWN = 0x0104 + WM_SYSKEYUP = 0x0105 WM_RBUTTONDOWN = 0x0204 WM_RBUTTONUP = 0x0205 WM_TIMER = 0x0113 @@ -159,6 +200,7 @@ const ( WS_CLIPCHILDREN = 0x00010000 WS_CLIPSIBLINGS = 0x04000000 + WS_MAXIMIZE = 0x01000000 WS_VISIBLE = 0x10000000 WS_OVERLAPPED = 0x00000000 WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | @@ -185,6 +227,19 @@ const ( GHND = 0x0042 CF_UNICODETEXT = 13 + IMAGE_BITMAP = 0 + IMAGE_ICON = 1 + IMAGE_CURSOR = 2 + + LR_CREATEDIBSECTION = 0x00002000 + LR_DEFAULTCOLOR = 0x00000000 + LR_DEFAULTSIZE = 0x00000040 + LR_LOADFROMFILE = 0x00000010 + LR_LOADMAP3DCOLORS = 0x00001000 + LR_LOADTRANSPARENT = 0x00000020 + LR_MONOCHROME = 0x00000001 + LR_SHARED = 0x00008000 + LR_VGACOLOR = 0x00000080 ) var ( @@ -207,12 +262,20 @@ var ( _GetClientRect = user32.NewProc("GetClientRect") _GetClipboardData = user32.NewProc("GetClipboardData") _GetDC = user32.NewProc("GetDC") + _GetDpiForWindow = user32.NewProc("GetDpiForWindow") _GetKeyState = user32.NewProc("GetKeyState") _GetMessage = user32.NewProc("GetMessageW") _GetMessageTime = user32.NewProc("GetMessageTime") + _GetMonitorInfo = user32.NewProc("GetMonitorInfoW") + _GetWindowLong = user32.NewProc("GetWindowLongPtrW") + _GetWindowLong32 = user32.NewProc("GetWindowLongW") + _GetWindowPlacement = user32.NewProc("GetWindowPlacement") _KillTimer = user32.NewProc("KillTimer") _LoadCursor = user32.NewProc("LoadCursorW") + _LoadImage = user32.NewProc("LoadImageW") _MonitorFromPoint = user32.NewProc("MonitorFromPoint") + _MonitorFromWindow = user32.NewProc("MonitorFromWindow") + _MoveWindow = user32.NewProc("MoveWindow") _MsgWaitForMultipleObjectsEx = user32.NewProc("MsgWaitForMultipleObjectsEx") _OpenClipboard = user32.NewProc("OpenClipboard") _PeekMessage = user32.NewProc("PeekMessageW") @@ -224,11 +287,17 @@ var ( _ScreenToClient = user32.NewProc("ScreenToClient") _ShowWindow = user32.NewProc("ShowWindow") _SetCapture = user32.NewProc("SetCapture") + _SetCursor = user32.NewProc("SetCursor") _SetClipboardData = user32.NewProc("SetClipboardData") _SetForegroundWindow = user32.NewProc("SetForegroundWindow") _SetFocus = user32.NewProc("SetFocus") _SetProcessDPIAware = user32.NewProc("SetProcessDPIAware") _SetTimer = user32.NewProc("SetTimer") + _SetWindowLong = user32.NewProc("SetWindowLongPtrW") + _SetWindowLong32 = user32.NewProc("SetWindowLongW") + _SetWindowPlacement = user32.NewProc("SetWindowPlacement") + _SetWindowPos = user32.NewProc("SetWindowPos") + _SetWindowText = user32.NewProc("SetWindowTextW") _TranslateMessage = user32.NewProc("TranslateMessage") _UnregisterClass = user32.NewProc("UnregisterClassW") _UpdateWindow = user32.NewProc("UpdateWindow") @@ -343,7 +412,7 @@ func getDpiForMonitor(hmonitor syscall.Handle, dpiType uint32) int { // GetSystemDPI returns the effective DPI of the system. func GetSystemDPI() int { - // Check for getDpiForMonitor, introduced in Windows 8.1. + // Check for GetDpiForMonitor, introduced in Windows 8.1. if _GetDpiForMonitor.Find() == nil { hmon := monitorFromPoint(Point{}, MONITOR_DEFAULTTOPRIMARY) return getDpiForMonitor(hmon, MDT_EFFECTIVE_DPI) @@ -377,6 +446,66 @@ func GetMessageTime() time.Duration { return time.Duration(r) * time.Millisecond } +// GetWindowDPI returns the effective DPI of the window. +func GetWindowDPI(hwnd syscall.Handle) int { + // Check for GetDpiForWindow, introduced in Windows 10. + if _GetDpiForWindow.Find() == nil { + dpi, _, _ := _GetDpiForWindow.Call(uintptr(hwnd)) + return int(dpi) + } else { + return GetSystemDPI() + } +} + +func GetWindowPlacement(hwnd syscall.Handle) *WindowPlacement { + var wp WindowPlacement + wp.length = uint32(unsafe.Sizeof(wp)) + _GetWindowPlacement.Call(uintptr(hwnd), uintptr(unsafe.Pointer(&wp))) + return &wp +} + +func GetMonitorInfo(hwnd syscall.Handle) MonitorInfo { + var mi MonitorInfo + mi.cbSize = uint32(unsafe.Sizeof(mi)) + v, _, _ := _MonitorFromWindow.Call(uintptr(hwnd), MONITOR_DEFAULTTOPRIMARY) + _GetMonitorInfo.Call(v, uintptr(unsafe.Pointer(&mi))) + return mi +} + +func GetWindowLong(hwnd syscall.Handle) (style uintptr) { + if runtime.GOARCH == "386" { + style, _, _ = _GetWindowLong32.Call(uintptr(hwnd), uintptr(GWL_STYLE)) + } else { + style, _, _ = _GetWindowLong.Call(uintptr(hwnd), uintptr(GWL_STYLE)) + } + return +} + +func SetWindowLong(hwnd syscall.Handle, idx uint32, style uintptr) { + if runtime.GOARCH == "386" { + _SetWindowLong32.Call(uintptr(hwnd), uintptr(idx), style) + } else { + _SetWindowLong.Call(uintptr(hwnd), uintptr(idx), style) + } +} + +func SetWindowPlacement(hwnd syscall.Handle, wp *WindowPlacement) { + _SetWindowPlacement.Call(uintptr(hwnd), uintptr(unsafe.Pointer(wp))) +} + +func SetWindowPos(hwnd syscall.Handle, hwndInsertAfter uint32, x, y, dx, dy int32, style uintptr) { + _SetWindowPos.Call(uintptr(hwnd), uintptr(hwndInsertAfter), + uintptr(x), uintptr(y), + uintptr(dx), uintptr(dy), + style, + ) +} + +func SetWindowText(hwnd syscall.Handle, title string) { + wname := syscall.StringToUTF16Ptr(title) + _SetWindowText.Call(uintptr(hwnd), uintptr(unsafe.Pointer(wname))) +} + func GlobalAlloc(size int) (syscall.Handle, error) { r, _, err := _GlobalAlloc.Call(GHND, uintptr(size)) if r == 0 { @@ -417,6 +546,22 @@ func LoadCursor(curID uint16) (syscall.Handle, error) { return syscall.Handle(h), nil } +func LoadImage(hInst syscall.Handle, res uint32, typ uint32, cx, cy int, fuload uint32) (syscall.Handle, error) { + h, _, err := _LoadImage.Call(uintptr(hInst), uintptr(res), uintptr(typ), uintptr(cx), uintptr(cy), uintptr(fuload)) + if h == 0 { + return 0, fmt.Errorf("LoadImageW failed: %v", err) + } + return syscall.Handle(h), nil +} + +func MoveWindow(hwnd syscall.Handle, x, y, width, height int32, repaint bool) { + var paint uintptr + if repaint { + paint = TRUE + } + _MoveWindow.Call(uintptr(hwnd), uintptr(x), uintptr(y), uintptr(width), uintptr(height), paint) +} + func monitorFromPoint(pt Point, flags uint32) syscall.Handle { r, _, _ := _MonitorFromPoint.Call(uintptr(pt.X), uintptr(pt.Y), uintptr(flags)) return syscall.Handle(r) @@ -500,6 +645,10 @@ func SetClipboardData(format uint32, mem syscall.Handle) error { return nil } +func SetCursor(h syscall.Handle) { + _SetCursor.Call(uintptr(h)) +} + func SetTimer(hwnd syscall.Handle, nIDEvent uintptr, uElapse uint32, timerProc uintptr) error { r, _, err := _SetTimer.Call(uintptr(hwnd), uintptr(nIDEvent), uintptr(uElapse), timerProc) if r == 0 { @@ -530,6 +679,10 @@ func UpdateWindow(hwnd syscall.Handle) { _UpdateWindow.Call(uintptr(hwnd)) } +func (p WindowPlacement) Rect() Rect { + return p.rcNormalPosition +} + // issue34474KeepAlive calls runtime.KeepAlive as a // workaround for golang.org/issue/34474. func issue34474KeepAlive(v interface{}) { diff --git a/vendor/gioui.org/app/internal/xkb/xkb_unix.go b/vendor/gioui.org/app/internal/xkb/xkb_unix.go index 3525692..68b1329 100644 --- a/vendor/gioui.org/app/internal/xkb/xkb_unix.go +++ b/vendor/gioui.org/app/internal/xkb/xkb_unix.go @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Unlicense OR MIT +//go:build (linux && !android) || freebsd || openbsd // +build linux,!android freebsd openbsd // Package xkb implements a Go interface for the X Keyboard Extension library. @@ -150,7 +151,7 @@ func (x *Context) Modifiers() key.Modifiers { return mods } -func (x *Context) DispatchKey(keyCode uint32) (events []event.Event) { +func (x *Context) DispatchKey(keyCode uint32, state key.State) (events []event.Event) { if x.state == nil { return } @@ -163,6 +164,7 @@ func (x *Context) DispatchKey(keyCode uint32) (events []event.Event) { cmd := key.Event{ Name: name, Modifiers: x.Modifiers(), + State: state, } // Ensure that a physical backtab key is translated to // Shift-Tab. @@ -185,7 +187,10 @@ func (x *Context) DispatchKey(keyCode uint32) (events []event.Event) { C.xkb_compose_state_reset(x.compState) str = x.utf8Buf[:size] case C.XKB_COMPOSE_NOTHING: - str = x.charsForKeycode(kc) + mod := x.Modifiers() + if mod&(key.ModCtrl|key.ModAlt|key.ModSuper) == 0 { + str = x.charsForKeycode(kc) + } } // Report only printable runes. var n int @@ -198,7 +203,7 @@ func (x *Context) DispatchKey(keyCode uint32) (events []event.Event) { str = str[:len(str)-s] } } - if len(str) > 0 { + if state == key.Press && len(str) > 0 { events = append(events, key.EditEvent{Text: string(str)}) } return @@ -230,7 +235,7 @@ func convertKeysym(s C.xkb_keysym_t) (string, bool) { if 'a' <= s && s <= 'z' { return string(rune(s - 'a' + 'A')), true } - if ' ' <= s && s <= '~' { + if ' ' < s && s <= '~' { return string(rune(s)), true } var n string @@ -288,7 +293,7 @@ func convertKeysym(s C.xkb_keysym_t) (string, bool) { case C.XKB_KEY_Tab, C.XKB_KEY_KP_Tab, C.XKB_KEY_ISO_Left_Tab: n = key.NameTab case 0x20, C.XKB_KEY_KP_Space: - n = "Space" + n = key.NameSpace default: return "", false } diff --git a/vendor/gioui.org/app/loop.go b/vendor/gioui.org/app/loop.go deleted file mode 100644 index 0b2195a..0000000 --- a/vendor/gioui.org/app/loop.go +++ /dev/null @@ -1,157 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package app - -import ( - "image" - "runtime" - - "gioui.org/app/internal/window" - "gioui.org/gpu" - "gioui.org/op" -) - -type renderLoop struct { - summary string - drawing bool - err error - - frames chan frame - results chan frameResult - refresh chan struct{} - refreshErr chan error - ack chan struct{} - stop chan struct{} - stopped chan struct{} -} - -type frame struct { - viewport image.Point - ops *op.Ops -} - -type frameResult struct { - profile string - err error -} - -func newLoop(ctx window.Context) (*renderLoop, error) { - l := &renderLoop{ - frames: make(chan frame), - results: make(chan frameResult), - refresh: make(chan struct{}), - refreshErr: make(chan error), - // Ack is buffered so GPU commands can be issued after - // ack'ing the frame. - ack: make(chan struct{}, 1), - stop: make(chan struct{}), - stopped: make(chan struct{}), - } - if err := l.renderLoop(ctx); err != nil { - return nil, err - } - return l, nil -} - -func (l *renderLoop) renderLoop(ctx window.Context) error { - // GL Operations must happen on a single OS thread, so - // pass initialization result through a channel. - initErr := make(chan error) - go func() { - defer close(l.stopped) - runtime.LockOSThread() - // Don't UnlockOSThread to avoid reuse by the Go runtime. - - if err := ctx.MakeCurrent(); err != nil { - initErr <- err - return - } - b, err := ctx.Backend() - if err != nil { - initErr <- err - return - } - g, err := gpu.New(b) - if err != nil { - initErr <- err - return - } - defer ctx.Release() - initErr <- nil - loop: - for { - select { - case <-l.refresh: - l.refreshErr <- ctx.MakeCurrent() - case frame := <-l.frames: - ctx.Lock() - g.Collect(frame.viewport, frame.ops) - // Signal that we're done with the frame ops. - l.ack <- struct{}{} - g.BeginFrame() - var res frameResult - res.err = ctx.Present() - g.EndFrame() - res.profile = g.Profile() - ctx.Unlock() - l.results <- res - case <-l.stop: - break loop - } - } - }() - return <-initErr -} - -func (l *renderLoop) Release() { - // Flush error. - l.Flush() - close(l.stop) - <-l.stopped - l.stop = nil -} - -func (l *renderLoop) Flush() error { - if l.drawing { - st := <-l.results - l.setErr(st.err) - if st.profile != "" { - l.summary = st.profile - } - l.drawing = false - } - return l.err -} - -func (l *renderLoop) Summary() string { - return l.summary -} - -func (l *renderLoop) Refresh() { - if l.err != nil { - return - } - // Make sure any pending frame is complete. - l.Flush() - l.refresh <- struct{}{} - l.setErr(<-l.refreshErr) -} - -// Draw initiates a draw of a frame. It returns a channel -// than signals when the frame is no longer being accessed. -func (l *renderLoop) Draw(viewport image.Point, frameOps *op.Ops) <-chan struct{} { - if l.err != nil { - l.ack <- struct{}{} - return l.ack - } - l.Flush() - l.frames <- frame{viewport, frameOps} - l.drawing = true - return l.ack -} - -func (l *renderLoop) setErr(err error) { - if l.err == nil { - l.err = err - } -} diff --git a/vendor/gioui.org/app/metal_darwin.go b/vendor/gioui.org/app/metal_darwin.go new file mode 100644 index 0000000..06b89f3 --- /dev/null +++ b/vendor/gioui.org/app/metal_darwin.go @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build !nometal +// +build !nometal + +package app + +import ( + "errors" + + "gioui.org/gpu" +) + +/* +#cgo CFLAGS: -Werror -xobjective-c -fmodules -fobjc-arc + +@import Metal; +@import QuartzCore.CAMetalLayer; + +#include <CoreFoundation/CoreFoundation.h> + +static CFTypeRef createMetalDevice(void) { + @autoreleasepool { + id<MTLDevice> dev = MTLCreateSystemDefaultDevice(); + return CFBridgingRetain(dev); + } +} + +static void setupLayer(CFTypeRef layerRef, CFTypeRef devRef) { + @autoreleasepool { + CAMetalLayer *layer = (__bridge CAMetalLayer *)layerRef; + id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef; + layer.device = dev; + // Package gpu assumes an sRGB-encoded framebuffer. + layer.pixelFormat = MTLPixelFormatBGRA8Unorm_sRGB; + if (@available(iOS 11.0, *)) { + // Never let nextDrawable time out and return nil. + layer.allowsNextDrawableTimeout = NO; + } + } +} + +static CFTypeRef nextDrawable(CFTypeRef layerRef) { + @autoreleasepool { + CAMetalLayer *layer = (__bridge CAMetalLayer *)layerRef; + return CFBridgingRetain([layer nextDrawable]); + } +} + +static CFTypeRef drawableTexture(CFTypeRef drawableRef) { + @autoreleasepool { + id<CAMetalDrawable> drawable = (__bridge id<CAMetalDrawable>)drawableRef; + return CFBridgingRetain(drawable.texture); + } +} + +static void presentDrawable(CFTypeRef queueRef, CFTypeRef drawableRef) { + @autoreleasepool { + id<MTLDrawable> drawable = (__bridge id<MTLDrawable>)drawableRef; + id<MTLCommandQueue> queue = (__bridge id<MTLCommandQueue>)queueRef; + id<MTLCommandBuffer> cmdBuffer = [queue commandBuffer]; + [cmdBuffer presentDrawable:drawable]; + [cmdBuffer commit]; + } +} + +static CFTypeRef newCommandQueue(CFTypeRef devRef) { + @autoreleasepool { + id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef; + return CFBridgingRetain([dev newCommandQueue]); + } +} +*/ +import "C" + +type mtlContext struct { + dev C.CFTypeRef + view C.CFTypeRef + layer C.CFTypeRef + queue C.CFTypeRef + drawable C.CFTypeRef + texture C.CFTypeRef +} + +func newMtlContext(w *window) (*mtlContext, error) { + dev := C.createMetalDevice() + if dev == 0 { + return nil, errors.New("metal: MTLCreateSystemDefaultDevice failed") + } + view := w.contextView() + layer := getMetalLayer(view) + if layer == 0 { + C.CFRelease(dev) + return nil, errors.New("metal: CAMetalLayer construction failed") + } + queue := C.newCommandQueue(dev) + if layer == 0 { + C.CFRelease(dev) + C.CFRelease(layer) + return nil, errors.New("metal: [MTLDevice newCommandQueue] failed") + } + C.setupLayer(layer, dev) + c := &mtlContext{ + dev: dev, + view: view, + layer: layer, + queue: queue, + } + return c, nil +} + +func (c *mtlContext) RenderTarget() (gpu.RenderTarget, error) { + if c.drawable != 0 || c.texture != 0 { + return nil, errors.New("metal:a previous RenderTarget wasn't Presented") + } + c.drawable = C.nextDrawable(c.layer) + if c.drawable == 0 { + return nil, errors.New("metal: [CAMetalLayer nextDrawable] failed") + } + c.texture = C.drawableTexture(c.drawable) + if c.texture == 0 { + return nil, errors.New("metal: CADrawable.texture is nil") + } + return gpu.MetalRenderTarget{ + Texture: uintptr(c.texture), + }, nil +} + +func (c *mtlContext) API() gpu.API { + return gpu.Metal{ + Device: uintptr(c.dev), + Queue: uintptr(c.queue), + PixelFormat: int(C.MTLPixelFormatBGRA8Unorm_sRGB), + } +} + +func (c *mtlContext) Release() { + C.CFRelease(c.queue) + C.CFRelease(c.dev) + C.CFRelease(c.layer) + if c.drawable != 0 { + C.CFRelease(c.drawable) + } + if c.texture != 0 { + C.CFRelease(c.texture) + } + *c = mtlContext{} +} + +func (c *mtlContext) Present() error { + C.CFRelease(c.texture) + c.texture = 0 + C.presentDrawable(c.queue, c.drawable) + C.CFRelease(c.drawable) + c.drawable = 0 + return nil +} + +func (c *mtlContext) Lock() error { + return nil +} + +func (c *mtlContext) Unlock() {} + +func (c *mtlContext) Refresh() error { + resizeDrawable(c.view, c.layer) + return nil +} + +func (w *window) NewContext() (context, error) { + return newMtlContext(w) +} diff --git a/vendor/gioui.org/app/metal_ios.go b/vendor/gioui.org/app/metal_ios.go new file mode 100644 index 0000000..860ba1a --- /dev/null +++ b/vendor/gioui.org/app/metal_ios.go @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build !nometal +// +build !nometal + +package app + +/* +#cgo CFLAGS: -Werror -xobjective-c -fmodules -fobjc-arc + +@import UIKit; + +@import QuartzCore.CAMetalLayer; + +#include <CoreFoundation/CoreFoundation.h> + +Class gio_layerClass(void) { + return [CAMetalLayer class]; +} + +static CFTypeRef getMetalLayer(CFTypeRef viewRef) { + @autoreleasepool { + UIView *view = (__bridge UIView *)viewRef; + return CFBridgingRetain(view.layer); + } +} + +static void resizeDrawable(CFTypeRef viewRef, CFTypeRef layerRef) { + @autoreleasepool { + UIView *view = (__bridge UIView *)viewRef; + CAMetalLayer *layer = (__bridge CAMetalLayer *)layerRef; + layer.contentsScale = view.contentScaleFactor; + CGSize size = layer.bounds.size; + size.width *= layer.contentsScale; + size.height *= layer.contentsScale; + layer.drawableSize = size; + } +} +*/ +import "C" + +func getMetalLayer(view C.CFTypeRef) C.CFTypeRef { + return C.getMetalLayer(view) +} + +func resizeDrawable(view, layer C.CFTypeRef) { + C.resizeDrawable(view, layer) +} diff --git a/vendor/gioui.org/app/metal_macos.go b/vendor/gioui.org/app/metal_macos.go new file mode 100644 index 0000000..7db06a9 --- /dev/null +++ b/vendor/gioui.org/app/metal_macos.go @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build darwin && !ios && !nometal +// +build darwin,!ios,!nometal + +package app + +/* +#cgo CFLAGS: -Werror -xobjective-c -fmodules -fobjc-arc + +@import AppKit; + +@import QuartzCore.CAMetalLayer; + +#include <CoreFoundation/CoreFoundation.h> + +CALayer *gio_layerFactory(void) { + @autoreleasepool { + return [CAMetalLayer layer]; + } +} + +static CFTypeRef getMetalLayer(CFTypeRef viewRef) { + @autoreleasepool { + NSView *view = (__bridge NSView *)viewRef; + return CFBridgingRetain(view.layer); + } +} + +static void resizeDrawable(CFTypeRef viewRef, CFTypeRef layerRef) { + @autoreleasepool { + NSView *view = (__bridge NSView *)viewRef; + CAMetalLayer *layer = (__bridge CAMetalLayer *)layerRef; + CGSize size = layer.bounds.size; + size.width *= layer.contentsScale; + size.height *= layer.contentsScale; + layer.drawableSize = size; + } +} +*/ +import "C" + +func getMetalLayer(view C.CFTypeRef) C.CFTypeRef { + return C.getMetalLayer(view) +} + +func resizeDrawable(view, layer C.CFTypeRef) { + C.resizeDrawable(view, layer) +} diff --git a/vendor/gioui.org/app/os.go b/vendor/gioui.org/app/os.go new file mode 100644 index 0000000..fc5c266 --- /dev/null +++ b/vendor/gioui.org/app/os.go @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +// package app implements platform specific windows +// and GPU contexts. +package app + +import ( + "errors" + "image" + "image/color" + + "gioui.org/io/key" + + "gioui.org/gpu" + "gioui.org/io/pointer" + "gioui.org/io/system" + "gioui.org/unit" +) + +type size struct { + Width unit.Value + Height unit.Value +} + +// errOutOfDate is reported when the GPU surface dimensions or properties no +// longer match the window. +var errOutOfDate = errors.New("app: GPU surface out of date") + +// Config describes a Window configuration. +type Config struct { + // Size is the window dimensions (Width, Height). + Size image.Point + // MaxSize is the window maximum allowed dimensions. + MaxSize image.Point + // MinSize is the window minimum allowed dimensions. + MinSize image.Point + // Title is the window title displayed in its decoration bar. + Title string + // WindowMode is the window mode. + Mode WindowMode + // StatusColor is the color of the Android status bar. + StatusColor color.NRGBA + // NavigationColor is the color of the navigation bar + // on Android, or the address bar in browsers. + NavigationColor color.NRGBA + // Orientation is the current window orientation. + Orientation Orientation + // CustomRenderer is true when the window content is rendered by the + // client. + CustomRenderer bool +} + +// ConfigEvent is sent whenever the configuration of a Window changes. +type ConfigEvent struct { + Config Config +} + +func (c *Config) apply(m unit.Metric, options []Option) { + for _, o := range options { + o(m, c) + } +} + +type wakeupEvent struct{} + +// WindowMode is the window mode (WindowMode.Option sets it). +// +// Supported platforms are macOS, X11, Windows, Android and JS. +type WindowMode uint8 + +const ( + // Windowed is the normal window mode with OS specific window decorations. + Windowed WindowMode = iota + // Fullscreen is the full screen window mode. + Fullscreen +) + +func (m WindowMode) Option() Option { + return func(_ unit.Metric, cnf *Config) { + cnf.Mode = m + } +} + +func (m WindowMode) String() string { + switch m { + case Windowed: + return "windowed" + case Fullscreen: + return "fullscreen" + } + return "" +} + +// Orientation is the orientation of the app (Orientation.Option sets it). +// +// Supported platforms are Android and JS. +type Orientation uint8 + +const ( + // AnyOrientation allows the window to be freely orientated. + AnyOrientation Orientation = iota + // LandscapeOrientation constrains the window to landscape orientations. + LandscapeOrientation + // PortraitOrientation constrains the window to portrait orientations. + PortraitOrientation +) + +func (o Orientation) Option() Option { + return func(_ unit.Metric, cnf *Config) { + cnf.Orientation = o + } +} + +func (o Orientation) String() string { + switch o { + case AnyOrientation: + return "any" + case LandscapeOrientation: + return "landscape" + case PortraitOrientation: + return "portrait" + } + return "" +} + +type frameEvent struct { + system.FrameEvent + + Sync bool +} + +type context interface { + API() gpu.API + RenderTarget() (gpu.RenderTarget, error) + Present() error + Refresh() error + Release() + Lock() error + Unlock() +} + +// Driver is the interface for the platform implementation +// of a window. +type driver interface { + // SetAnimating sets the animation flag. When the window is animating, + // FrameEvents are delivered as fast as the display can handle them. + SetAnimating(anim bool) + + // ShowTextInput updates the virtual keyboard state. + ShowTextInput(show bool) + + SetInputHint(mode key.InputHint) + + NewContext() (context, error) + + // ReadClipboard requests the clipboard content. + ReadClipboard() + // WriteClipboard requests a clipboard write. + WriteClipboard(s string) + + // Configure the window. + Configure([]Option) + + // SetCursor updates the current cursor to name. + SetCursor(name pointer.CursorName) + + // Raise the window at the top. + Raise() + + // Close the window. + Close() + + // Wakeup wakes up the event loop and sends a WakeupEvent. + Wakeup() + + // Maximize will make the window as large as possible, but keep the frame decorations. + Maximize() + // Center will place the window at monitor center. + Center() +} + +type windowRendezvous struct { + in chan windowAndConfig + out chan windowAndConfig + errs chan error +} + +type windowAndConfig struct { + window *callbacks + options []Option +} + +func newWindowRendezvous() *windowRendezvous { + wr := &windowRendezvous{ + in: make(chan windowAndConfig), + out: make(chan windowAndConfig), + errs: make(chan error), + } + go func() { + var main windowAndConfig + var out chan windowAndConfig + for { + select { + case w := <-wr.in: + var err error + if main.window != nil { + err = errors.New("multiple windows are not supported") + } + wr.errs <- err + main = w + out = wr.out + case out <- main: + } + } + }() + return wr +} + +func (wakeupEvent) ImplementsEvent() {} +func (ConfigEvent) ImplementsEvent() {} diff --git a/vendor/gioui.org/app/os_android.go b/vendor/gioui.org/app/os_android.go new file mode 100644 index 0000000..51d8289 --- /dev/null +++ b/vendor/gioui.org/app/os_android.go @@ -0,0 +1,1283 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package app + +/* +#cgo CFLAGS: -Werror +#cgo LDFLAGS: -landroid + +#include <android/native_window_jni.h> +#include <android/configuration.h> +#include <android/keycodes.h> +#include <android/input.h> +#include <stdlib.h> + +static jint jni_GetEnv(JavaVM *vm, JNIEnv **env, jint version) { + return (*vm)->GetEnv(vm, (void **)env, version); +} + +static jint jni_GetJavaVM(JNIEnv *env, JavaVM **jvm) { + return (*env)->GetJavaVM(env, jvm); +} + +static jint jni_AttachCurrentThread(JavaVM *vm, JNIEnv **p_env, void *thr_args) { + return (*vm)->AttachCurrentThread(vm, p_env, thr_args); +} + +static jint jni_DetachCurrentThread(JavaVM *vm) { + return (*vm)->DetachCurrentThread(vm); +} + +static jobject jni_NewGlobalRef(JNIEnv *env, jobject obj) { + return (*env)->NewGlobalRef(env, obj); +} + +static void jni_DeleteGlobalRef(JNIEnv *env, jobject obj) { + (*env)->DeleteGlobalRef(env, obj); +} + +static jclass jni_GetObjectClass(JNIEnv *env, jobject obj) { + return (*env)->GetObjectClass(env, obj); +} + +static jmethodID jni_GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig) { + return (*env)->GetMethodID(env, clazz, name, sig); +} + +static jmethodID jni_GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig) { + return (*env)->GetStaticMethodID(env, clazz, name, sig); +} + +static jfloat jni_CallFloatMethod(JNIEnv *env, jobject obj, jmethodID methodID) { + return (*env)->CallFloatMethod(env, obj, methodID); +} + +static jint jni_CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID) { + return (*env)->CallIntMethod(env, obj, methodID); +} + +static void jni_CallStaticVoidMethodA(JNIEnv *env, jclass cls, jmethodID methodID, const jvalue *args) { + (*env)->CallStaticVoidMethodA(env, cls, methodID, args); +} + +static void jni_CallVoidMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args) { + (*env)->CallVoidMethodA(env, obj, methodID, args); +} + +static jboolean jni_CallBooleanMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args) { + return (*env)->CallBooleanMethodA(env, obj, methodID, args); +} + +static jbyte *jni_GetByteArrayElements(JNIEnv *env, jbyteArray arr) { + return (*env)->GetByteArrayElements(env, arr, NULL); +} + +static void jni_ReleaseByteArrayElements(JNIEnv *env, jbyteArray arr, jbyte *bytes) { + (*env)->ReleaseByteArrayElements(env, arr, bytes, JNI_ABORT); +} + +static jsize jni_GetArrayLength(JNIEnv *env, jbyteArray arr) { + return (*env)->GetArrayLength(env, arr); +} + +static jstring jni_NewString(JNIEnv *env, const jchar *unicodeChars, jsize len) { + return (*env)->NewString(env, unicodeChars, len); +} + +static jsize jni_GetStringLength(JNIEnv *env, jstring str) { + return (*env)->GetStringLength(env, str); +} + +static const jchar *jni_GetStringChars(JNIEnv *env, jstring str) { + return (*env)->GetStringChars(env, str, NULL); +} + +static jthrowable jni_ExceptionOccurred(JNIEnv *env) { + return (*env)->ExceptionOccurred(env); +} + +static void jni_ExceptionClear(JNIEnv *env) { + (*env)->ExceptionClear(env); +} + +static jobject jni_CallObjectMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args) { + return (*env)->CallObjectMethodA(env, obj, method, args); +} + +static jobject jni_CallStaticObjectMethodA(JNIEnv *env, jclass cls, jmethodID method, jvalue *args) { + return (*env)->CallStaticObjectMethodA(env, cls, method, args); +} + +static jclass jni_FindClass(JNIEnv *env, char *name) { + return (*env)->FindClass(env, name); +} + +static jobject jni_NewObjectA(JNIEnv *env, jclass cls, jmethodID cons, jvalue *args) { + return (*env)->NewObjectA(env, cls, cons, args); +} +*/ +import "C" + +import ( + "errors" + "fmt" + "image" + "image/color" + "os" + "path/filepath" + "reflect" + "runtime" + "runtime/debug" + "sync" + "time" + "unicode/utf16" + "unsafe" + + "gioui.org/internal/f32color" + + "gioui.org/f32" + "gioui.org/io/clipboard" + "gioui.org/io/key" + "gioui.org/io/pointer" + "gioui.org/io/router" + "gioui.org/io/semantic" + "gioui.org/io/system" + "gioui.org/unit" +) + +type window struct { + callbacks *callbacks + + view C.jobject + + dpi int + fontScale float32 + insets system.Insets + + stage system.Stage + started bool + animating bool + + win *C.ANativeWindow + config Config + + semantic struct { + hoverID router.SemanticID + rootID router.SemanticID + focusID router.SemanticID + diffs []router.SemanticID + } +} + +// gioView hold cached JNI methods for GioView. +var gioView struct { + once sync.Once + getDensity C.jmethodID + getFontScale C.jmethodID + showTextInput C.jmethodID + hideTextInput C.jmethodID + setInputHint C.jmethodID + postInvalidate C.jmethodID // requests draw, called from non-UI thread + invalidate C.jmethodID // requests draw, called from UI thread + setCursor C.jmethodID + setOrientation C.jmethodID + setNavigationColor C.jmethodID + setStatusColor C.jmethodID + setFullscreen C.jmethodID + unregister C.jmethodID + sendA11yEvent C.jmethodID + sendA11yChange C.jmethodID + isA11yActive C.jmethodID +} + +// ViewEvent is sent whenever the Window's underlying Android view +// changes. +type ViewEvent struct { + // View is a JNI global reference to the android.view.View + // instance backing the Window. The reference is valid until + // the next ViewEvent is received. + // A zero View means that there is currently no view attached. + View uintptr +} + +type jvalue uint64 // The largest JNI type fits in 64 bits. + +var dataDirChan = make(chan string, 1) + +var android struct { + // mu protects all fields of this structure. However, once a + // non-nil jvm is returned from javaVM, all the other fields may + // be accessed unlocked. + mu sync.Mutex + jvm *C.JavaVM + + // appCtx is the global Android App context. + appCtx C.jobject + // gioCls is the class of the Gio class. + gioCls C.jclass + + mwriteClipboard C.jmethodID + mreadClipboard C.jmethodID + mwakeupMainThread C.jmethodID + + // android.view.accessibility.AccessibilityNodeInfo class. + accessibilityNodeInfo struct { + cls C.jclass + // addChild(View, int) + addChild C.jmethodID + // setBoundsInScreen(Rect) + setBoundsInScreen C.jmethodID + // setText(CharSequence) + setText C.jmethodID + // setContentDescription(CharSequence) + setContentDescription C.jmethodID + // setParent(View, int) + setParent C.jmethodID + // addAction(int) + addAction C.jmethodID + // setClassName(CharSequence) + setClassName C.jmethodID + // setCheckable(boolean) + setCheckable C.jmethodID + // setSelected(boolean) + setSelected C.jmethodID + // setChecked(boolean) + setChecked C.jmethodID + // setEnabled(boolean) + setEnabled C.jmethodID + // setAccessibilityFocused(boolean) + setAccessibilityFocused C.jmethodID + } + + // android.graphics.Rect class. + rect struct { + cls C.jclass + // (int, int, int, int) constructor. + cons C.jmethodID + } + + strings struct { + // "android.view.View" + androidViewView C.jstring + // "android.widget.Button" + androidWidgetButton C.jstring + // "android.widget.CheckBox" + androidWidgetCheckBox C.jstring + // "android.widget.EditText" + androidWidgetEditText C.jstring + // "android.widget.RadioButton" + androidWidgetRadioButton C.jstring + // "android.widget.Switch" + androidWidgetSwitch C.jstring + } +} + +// view maps from GioView JNI refenreces to windows. +var views = make(map[C.jlong]*window) + +var windows = make(map[*callbacks]*window) + +var mainWindow = newWindowRendezvous() + +var mainFuncs = make(chan func(env *C.JNIEnv), 1) + +var ( + dataDirOnce sync.Once + dataPath string +) + +var ( + newAndroidVulkanContext func(w *window) (context, error) + newAndroidGLESContext func(w *window) (context, error) +) + +// AccessibilityNodeProvider.HOST_VIEW_ID. +const HOST_VIEW_ID = -1 + +const ( + // AccessibilityEvent constants. + TYPE_VIEW_HOVER_ENTER = 128 + TYPE_VIEW_HOVER_EXIT = 256 +) + +const ( + // AccessibilityNodeInfo constants. + ACTION_ACCESSIBILITY_FOCUS = 64 + ACTION_CLEAR_ACCESSIBILITY_FOCUS = 128 + ACTION_CLICK = 16 +) + +func (w *window) NewContext() (context, error) { + funcs := []func(w *window) (context, error){newAndroidVulkanContext, newAndroidGLESContext} + var firstErr error + for _, f := range funcs { + if f == nil { + continue + } + c, err := f(w) + if err != nil { + if firstErr == nil { + firstErr = err + } + continue + } + return c, nil + } + if firstErr != nil { + return nil, firstErr + } + return nil, errors.New("x11: no available GPU backends") +} + +func dataDir() (string, error) { + dataDirOnce.Do(func() { + dataPath = <-dataDirChan + // Set XDG_CACHE_HOME to make os.UserCacheDir work. + if _, exists := os.LookupEnv("XDG_CACHE_HOME"); !exists { + cachePath := filepath.Join(dataPath, "cache") + os.Setenv("XDG_CACHE_HOME", cachePath) + } + // Set XDG_CONFIG_HOME to make os.UserConfigDir work. + if _, exists := os.LookupEnv("XDG_CONFIG_HOME"); !exists { + cfgPath := filepath.Join(dataPath, "config") + os.Setenv("XDG_CONFIG_HOME", cfgPath) + } + // Set HOME to make os.UserHomeDir work. + if _, exists := os.LookupEnv("HOME"); !exists { + os.Setenv("HOME", dataPath) + } + }) + return dataPath, nil +} + +func getMethodID(env *C.JNIEnv, class C.jclass, method, sig string) C.jmethodID { + m := C.CString(method) + defer C.free(unsafe.Pointer(m)) + s := C.CString(sig) + defer C.free(unsafe.Pointer(s)) + jm := C.jni_GetMethodID(env, class, m, s) + if err := exception(env); err != nil { + panic(err) + } + return jm +} + +func getStaticMethodID(env *C.JNIEnv, class C.jclass, method, sig string) C.jmethodID { + m := C.CString(method) + defer C.free(unsafe.Pointer(m)) + s := C.CString(sig) + defer C.free(unsafe.Pointer(s)) + jm := C.jni_GetStaticMethodID(env, class, m, s) + if err := exception(env); err != nil { + panic(err) + } + return jm +} + +//export Java_org_gioui_Gio_runGoMain +func Java_org_gioui_Gio_runGoMain(env *C.JNIEnv, class C.jclass, jdataDir C.jbyteArray, context C.jobject) { + initJVM(env, class, context) + dirBytes := C.jni_GetByteArrayElements(env, jdataDir) + if dirBytes == nil { + panic("runGoMain: GetByteArrayElements failed") + } + n := C.jni_GetArrayLength(env, jdataDir) + dataDir := C.GoStringN((*C.char)(unsafe.Pointer(dirBytes)), n) + dataDirChan <- dataDir + C.jni_ReleaseByteArrayElements(env, jdataDir, dirBytes) + + runMain() +} + +func initJVM(env *C.JNIEnv, gio C.jclass, ctx C.jobject) { + android.mu.Lock() + defer android.mu.Unlock() + if res := C.jni_GetJavaVM(env, &android.jvm); res != 0 { + panic("gio: GetJavaVM failed") + } + android.appCtx = C.jni_NewGlobalRef(env, ctx) + android.gioCls = C.jclass(C.jni_NewGlobalRef(env, C.jobject(gio))) + + cls := findClass(env, "android/view/accessibility/AccessibilityNodeInfo") + android.accessibilityNodeInfo.cls = C.jclass(C.jni_NewGlobalRef(env, C.jobject(cls))) + android.accessibilityNodeInfo.addChild = getMethodID(env, cls, "addChild", "(Landroid/view/View;I)V") + android.accessibilityNodeInfo.setBoundsInScreen = getMethodID(env, cls, "setBoundsInScreen", "(Landroid/graphics/Rect;)V") + android.accessibilityNodeInfo.setText = getMethodID(env, cls, "setText", "(Ljava/lang/CharSequence;)V") + android.accessibilityNodeInfo.setContentDescription = getMethodID(env, cls, "setContentDescription", "(Ljava/lang/CharSequence;)V") + android.accessibilityNodeInfo.setParent = getMethodID(env, cls, "setParent", "(Landroid/view/View;I)V") + android.accessibilityNodeInfo.addAction = getMethodID(env, cls, "addAction", "(I)V") + android.accessibilityNodeInfo.setClassName = getMethodID(env, cls, "setClassName", "(Ljava/lang/CharSequence;)V") + android.accessibilityNodeInfo.setCheckable = getMethodID(env, cls, "setCheckable", "(Z)V") + android.accessibilityNodeInfo.setSelected = getMethodID(env, cls, "setSelected", "(Z)V") + android.accessibilityNodeInfo.setChecked = getMethodID(env, cls, "setChecked", "(Z)V") + android.accessibilityNodeInfo.setEnabled = getMethodID(env, cls, "setEnabled", "(Z)V") + android.accessibilityNodeInfo.setAccessibilityFocused = getMethodID(env, cls, "setAccessibilityFocused", "(Z)V") + + cls = findClass(env, "android/graphics/Rect") + android.rect.cls = C.jclass(C.jni_NewGlobalRef(env, C.jobject(cls))) + android.rect.cons = getMethodID(env, cls, "<init>", "(IIII)V") + android.mwriteClipboard = getStaticMethodID(env, gio, "writeClipboard", "(Landroid/content/Context;Ljava/lang/String;)V") + android.mreadClipboard = getStaticMethodID(env, gio, "readClipboard", "(Landroid/content/Context;)Ljava/lang/String;") + android.mwakeupMainThread = getStaticMethodID(env, gio, "wakeupMainThread", "()V") + + intern := func(s string) C.jstring { + ref := C.jni_NewGlobalRef(env, C.jobject(javaString(env, s))) + return C.jstring(ref) + } + android.strings.androidViewView = intern("android.view.View") + android.strings.androidWidgetButton = intern("android.widget.Button") + android.strings.androidWidgetCheckBox = intern("android.widget.CheckBox") + android.strings.androidWidgetEditText = intern("android.widget.EditText") + android.strings.androidWidgetRadioButton = intern("android.widget.RadioButton") + android.strings.androidWidgetSwitch = intern("android.widget.Switch") +} + +// JavaVM returns the global JNI JavaVM. +func JavaVM() uintptr { + jvm := javaVM() + return uintptr(unsafe.Pointer(jvm)) +} + +func javaVM() *C.JavaVM { + android.mu.Lock() + defer android.mu.Unlock() + return android.jvm +} + +// AppContext returns the global Application context as a JNI jobject. +func AppContext() uintptr { + android.mu.Lock() + defer android.mu.Unlock() + return uintptr(android.appCtx) +} + +//export Java_org_gioui_GioView_onCreateView +func Java_org_gioui_GioView_onCreateView(env *C.JNIEnv, class C.jclass, view C.jobject) C.jlong { + gioView.once.Do(func() { + m := &gioView + m.getDensity = getMethodID(env, class, "getDensity", "()I") + m.getFontScale = getMethodID(env, class, "getFontScale", "()F") + m.showTextInput = getMethodID(env, class, "showTextInput", "()V") + m.hideTextInput = getMethodID(env, class, "hideTextInput", "()V") + m.setInputHint = getMethodID(env, class, "setInputHint", "(I)V") + m.postInvalidate = getMethodID(env, class, "postInvalidate", "()V") + m.invalidate = getMethodID(env, class, "invalidate", "()V") + m.setCursor = getMethodID(env, class, "setCursor", "(I)V") + m.setOrientation = getMethodID(env, class, "setOrientation", "(II)V") + m.setNavigationColor = getMethodID(env, class, "setNavigationColor", "(II)V") + m.setStatusColor = getMethodID(env, class, "setStatusColor", "(II)V") + m.setFullscreen = getMethodID(env, class, "setFullscreen", "(Z)V") + m.unregister = getMethodID(env, class, "unregister", "()V") + m.sendA11yEvent = getMethodID(env, class, "sendA11yEvent", "(II)V") + m.sendA11yChange = getMethodID(env, class, "sendA11yChange", "(I)V") + m.isA11yActive = getMethodID(env, class, "isA11yActive", "()Z") + }) + view = C.jni_NewGlobalRef(env, view) + wopts := <-mainWindow.out + w, ok := windows[wopts.window] + if !ok { + w = &window{ + callbacks: wopts.window, + } + windows[wopts.window] = w + } + if w.view != 0 { + w.detach(env) + } + w.view = view + w.callbacks.SetDriver(w) + handle := C.jlong(view) + views[handle] = w + w.loadConfig(env, class) + w.Configure(wopts.options) + w.setStage(system.StagePaused) + w.callbacks.Event(ViewEvent{View: uintptr(view)}) + return handle +} + +//export Java_org_gioui_GioView_onDestroyView +func Java_org_gioui_GioView_onDestroyView(env *C.JNIEnv, class C.jclass, handle C.jlong) { + w := views[handle] + w.detach(env) +} + +//export Java_org_gioui_GioView_onStopView +func Java_org_gioui_GioView_onStopView(env *C.JNIEnv, class C.jclass, handle C.jlong) { + w := views[handle] + w.started = false + w.setStage(system.StagePaused) +} + +//export Java_org_gioui_GioView_onStartView +func Java_org_gioui_GioView_onStartView(env *C.JNIEnv, class C.jclass, handle C.jlong) { + w := views[handle] + w.started = true + if w.win != nil { + w.setVisible(env) + } +} + +//export Java_org_gioui_GioView_onSurfaceDestroyed +func Java_org_gioui_GioView_onSurfaceDestroyed(env *C.JNIEnv, class C.jclass, handle C.jlong) { + w := views[handle] + w.win = nil + w.setStage(system.StagePaused) +} + +//export Java_org_gioui_GioView_onSurfaceChanged +func Java_org_gioui_GioView_onSurfaceChanged(env *C.JNIEnv, class C.jclass, handle C.jlong, surf C.jobject) { + w := views[handle] + w.win = C.ANativeWindow_fromSurface(env, surf) + if w.started { + w.setVisible(env) + } +} + +//export Java_org_gioui_GioView_onLowMemory +func Java_org_gioui_GioView_onLowMemory(env *C.JNIEnv, class C.jclass) { + runtime.GC() + debug.FreeOSMemory() +} + +//export Java_org_gioui_GioView_onConfigurationChanged +func Java_org_gioui_GioView_onConfigurationChanged(env *C.JNIEnv, class C.jclass, view C.jlong) { + w := views[view] + w.loadConfig(env, class) + if w.stage >= system.StageRunning { + w.draw(env, true) + } +} + +//export Java_org_gioui_GioView_onFrameCallback +func Java_org_gioui_GioView_onFrameCallback(env *C.JNIEnv, class C.jclass, view C.jlong) { + w, exist := views[view] + if !exist { + return + } + if w.stage < system.StageRunning { + return + } + if w.animating { + w.draw(env, false) + // Schedule the next draw immediately after this one. Since onFrameCallback runs + // on the UI thread, View.invalidate can be used here instead of postInvalidate. + callVoidMethod(env, w.view, gioView.invalidate) + } +} + +//export Java_org_gioui_GioView_onBack +func Java_org_gioui_GioView_onBack(env *C.JNIEnv, class C.jclass, view C.jlong) C.jboolean { + w := views[view] + ev := &system.CommandEvent{Type: system.CommandBack} + w.callbacks.Event(ev) + if ev.Cancel { + return C.JNI_TRUE + } + return C.JNI_FALSE +} + +//export Java_org_gioui_GioView_onFocusChange +func Java_org_gioui_GioView_onFocusChange(env *C.JNIEnv, class C.jclass, view C.jlong, focus C.jboolean) { + w := views[view] + w.callbacks.Event(key.FocusEvent{Focus: focus == C.JNI_TRUE}) +} + +//export Java_org_gioui_GioView_onWindowInsets +func Java_org_gioui_GioView_onWindowInsets(env *C.JNIEnv, class C.jclass, view C.jlong, top, right, bottom, left C.jint) { + w := views[view] + w.insets = system.Insets{ + Top: unit.Px(float32(top)), + Right: unit.Px(float32(right)), + Bottom: unit.Px(float32(bottom)), + Left: unit.Px(float32(left)), + } + if w.stage >= system.StageRunning { + w.draw(env, true) + } +} + +//export Java_org_gioui_GioView_initializeAccessibilityNodeInfo +func Java_org_gioui_GioView_initializeAccessibilityNodeInfo(env *C.JNIEnv, class C.jclass, view C.jlong, virtID, screenX, screenY C.jint, info C.jobject) C.jobject { + w := views[view] + semID := w.semIDFor(virtID) + sem, found := w.callbacks.LookupSemantic(semID) + if found { + off := f32.Pt(float32(screenX), float32(screenY)) + if err := w.initAccessibilityNodeInfo(env, sem, off, info); err != nil { + panic(err) + } + } + return info +} + +//export Java_org_gioui_GioView_onTouchExploration +func Java_org_gioui_GioView_onTouchExploration(env *C.JNIEnv, class C.jclass, view C.jlong, x, y C.jfloat) { + w := views[view] + semID, _ := w.callbacks.SemanticAt(f32.Pt(float32(x), float32(y))) + if w.semantic.hoverID == semID { + return + } + // Android expects ENTER before EXIT. + if semID != 0 { + callVoidMethod(env, w.view, gioView.sendA11yEvent, TYPE_VIEW_HOVER_ENTER, jvalue(w.virtualIDFor(semID))) + } + if prevID := w.semantic.hoverID; prevID != 0 { + callVoidMethod(env, w.view, gioView.sendA11yEvent, TYPE_VIEW_HOVER_EXIT, jvalue(w.virtualIDFor(prevID))) + } + w.semantic.hoverID = semID +} + +//export Java_org_gioui_GioView_onExitTouchExploration +func Java_org_gioui_GioView_onExitTouchExploration(env *C.JNIEnv, class C.jclass, view C.jlong) { + w := views[view] + if w.semantic.hoverID != 0 { + callVoidMethod(env, w.view, gioView.sendA11yEvent, TYPE_VIEW_HOVER_EXIT, jvalue(w.virtualIDFor(w.semantic.hoverID))) + w.semantic.hoverID = 0 + } +} + +//export Java_org_gioui_GioView_onA11yFocus +func Java_org_gioui_GioView_onA11yFocus(env *C.JNIEnv, class C.jclass, view C.jlong, virtID C.jint) { + w := views[view] + if semID := w.semIDFor(virtID); semID != w.semantic.focusID { + w.semantic.focusID = semID + // Android needs invalidate to refresh the TalkBack focus indicator. + callVoidMethod(env, w.view, gioView.invalidate) + } +} + +//export Java_org_gioui_GioView_onClearA11yFocus +func Java_org_gioui_GioView_onClearA11yFocus(env *C.JNIEnv, class C.jclass, view C.jlong, virtID C.jint) { + w := views[view] + if w.semantic.focusID == w.semIDFor(virtID) { + w.semantic.focusID = 0 + } +} + +func (w *window) initAccessibilityNodeInfo(env *C.JNIEnv, sem router.SemanticNode, off f32.Point, info C.jobject) error { + for _, ch := range sem.Children { + err := callVoidMethod(env, info, android.accessibilityNodeInfo.addChild, jvalue(w.view), jvalue(w.virtualIDFor(ch.ID))) + if err != nil { + return err + } + } + if sem.ParentID != 0 { + if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setParent, jvalue(w.view), jvalue(w.virtualIDFor(sem.ParentID))); err != nil { + return err + } + b := sem.Desc.Bounds.Add(off) + rect, err := newObject(env, android.rect.cls, android.rect.cons, + jvalue(b.Min.X), + jvalue(b.Min.Y), + jvalue(b.Max.X), + jvalue(b.Max.Y), + ) + if err != nil { + return err + } + if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setBoundsInScreen, jvalue(rect)); err != nil { + return err + } + } + d := sem.Desc + if l := d.Label; l != "" { + jlbl := javaString(env, l) + if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setText, jvalue(jlbl)); err != nil { + return err + } + } + if d.Description != "" { + jd := javaString(env, d.Description) + if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setContentDescription, jvalue(jd)); err != nil { + return err + } + } + addAction := func(act C.jint) { + if err := callVoidMethod(env, info, android.accessibilityNodeInfo.addAction, jvalue(act)); err != nil { + panic(err) + } + } + if d.Gestures&router.ClickGesture != 0 { + addAction(ACTION_CLICK) + } + clsName := android.strings.androidViewView + selectMethod := android.accessibilityNodeInfo.setChecked + checkable := false + switch d.Class { + case semantic.Button: + clsName = android.strings.androidWidgetButton + case semantic.CheckBox: + checkable = true + clsName = android.strings.androidWidgetCheckBox + case semantic.Editor: + clsName = android.strings.androidWidgetEditText + case semantic.RadioButton: + selectMethod = android.accessibilityNodeInfo.setSelected + clsName = android.strings.androidWidgetRadioButton + case semantic.Switch: + checkable = true + clsName = android.strings.androidWidgetSwitch + } + if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setClassName, jvalue(clsName)); err != nil { + panic(err) + } + if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setCheckable, jvalue(javaBool(checkable))); err != nil { + panic(err) + } + if err := callVoidMethod(env, info, selectMethod, jvalue(javaBool(d.Selected))); err != nil { + panic(err) + } + if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setEnabled, jvalue(javaBool(!d.Disabled))); err != nil { + panic(err) + } + isFocus := w.semantic.focusID == sem.ID + if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setAccessibilityFocused, jvalue(javaBool(isFocus))); err != nil { + panic(err) + } + if isFocus { + addAction(ACTION_CLEAR_ACCESSIBILITY_FOCUS) + } else { + addAction(ACTION_ACCESSIBILITY_FOCUS) + } + return nil +} + +func (w *window) virtualIDFor(id router.SemanticID) C.jint { + // TODO: Android virtual IDs are 32-bit Java integers, but childID is a int64. + if id == w.semantic.rootID { + return HOST_VIEW_ID + } + return C.jint(id) +} + +func (w *window) semIDFor(virtID C.jint) router.SemanticID { + if virtID == HOST_VIEW_ID { + return w.semantic.rootID + } + return router.SemanticID(virtID) +} + +func (w *window) detach(env *C.JNIEnv) { + callVoidMethod(env, w.view, gioView.unregister) + w.callbacks.Event(ViewEvent{}) + w.callbacks.SetDriver(nil) + delete(views, C.jlong(w.view)) + C.jni_DeleteGlobalRef(env, w.view) + w.view = 0 +} + +func (w *window) setVisible(env *C.JNIEnv) { + width, height := C.ANativeWindow_getWidth(w.win), C.ANativeWindow_getHeight(w.win) + if width == 0 || height == 0 { + return + } + w.setStage(system.StageRunning) + w.draw(env, true) +} + +func (w *window) setStage(stage system.Stage) { + if stage == w.stage { + return + } + w.stage = stage + w.callbacks.Event(system.StageEvent{stage}) +} + +func (w *window) setVisual(visID int) error { + if C.ANativeWindow_setBuffersGeometry(w.win, 0, 0, C.int32_t(visID)) != 0 { + return errors.New("ANativeWindow_setBuffersGeometry failed") + } + return nil +} + +func (w *window) nativeWindow() (*C.ANativeWindow, int, int) { + width, height := C.ANativeWindow_getWidth(w.win), C.ANativeWindow_getHeight(w.win) + return w.win, int(width), int(height) +} + +func (w *window) loadConfig(env *C.JNIEnv, class C.jclass) { + dpi := int(C.jni_CallIntMethod(env, w.view, gioView.getDensity)) + w.fontScale = float32(C.jni_CallFloatMethod(env, w.view, gioView.getFontScale)) + switch dpi { + case C.ACONFIGURATION_DENSITY_NONE, + C.ACONFIGURATION_DENSITY_DEFAULT, + C.ACONFIGURATION_DENSITY_ANY: + // Assume standard density. + w.dpi = C.ACONFIGURATION_DENSITY_MEDIUM + default: + w.dpi = int(dpi) + } +} + +func (w *window) SetAnimating(anim bool) { + w.animating = anim + if anim { + runInJVM(javaVM(), func(env *C.JNIEnv) { + callVoidMethod(env, w.view, gioView.postInvalidate) + }) + } +} + +func (w *window) draw(env *C.JNIEnv, sync bool) { + size := image.Pt(int(C.ANativeWindow_getWidth(w.win)), int(C.ANativeWindow_getHeight(w.win))) + if size != w.config.Size { + w.config.Size = size + w.callbacks.Event(ConfigEvent{Config: w.config}) + } + if size.X == 0 || size.Y == 0 { + return + } + const inchPrDp = 1.0 / 160 + ppdp := float32(w.dpi) * inchPrDp + w.callbacks.Event(frameEvent{ + FrameEvent: system.FrameEvent{ + Now: time.Now(), + Size: w.config.Size, + Insets: w.insets, + Metric: unit.Metric{ + PxPerDp: ppdp, + PxPerSp: w.fontScale * ppdp, + }, + }, + Sync: sync, + }) + a11yActive, err := callBooleanMethod(env, w.view, gioView.isA11yActive) + if err != nil { + panic(err) + } + if a11yActive { + if newR, oldR := w.callbacks.SemanticRoot(), w.semantic.rootID; newR != oldR { + // Remap focus and hover. + if oldR == w.semantic.hoverID { + w.semantic.hoverID = newR + } + if oldR == w.semantic.focusID { + w.semantic.focusID = newR + } + w.semantic.rootID = newR + callVoidMethod(env, w.view, gioView.sendA11yChange, jvalue(w.virtualIDFor(newR))) + } + w.semantic.diffs = w.callbacks.AppendSemanticDiffs(w.semantic.diffs[:0]) + for _, id := range w.semantic.diffs { + callVoidMethod(env, w.view, gioView.sendA11yChange, jvalue(w.virtualIDFor(id))) + } + } +} + +type keyMapper func(devId, keyCode C.int32_t) rune + +func runInJVM(jvm *C.JavaVM, f func(env *C.JNIEnv)) { + if jvm == nil { + panic("nil JVM") + } + runtime.LockOSThread() + defer runtime.UnlockOSThread() + var env *C.JNIEnv + if res := C.jni_GetEnv(jvm, &env, C.JNI_VERSION_1_6); res != C.JNI_OK { + if res != C.JNI_EDETACHED { + panic(fmt.Errorf("JNI GetEnv failed with error %d", res)) + } + if C.jni_AttachCurrentThread(jvm, &env, nil) != C.JNI_OK { + panic(errors.New("runInJVM: AttachCurrentThread failed")) + } + defer C.jni_DetachCurrentThread(jvm) + } + + f(env) +} + +func convertKeyCode(code C.jint) (string, bool) { + var n string + switch code { + case C.AKEYCODE_DPAD_UP: + n = key.NameUpArrow + case C.AKEYCODE_DPAD_DOWN: + n = key.NameDownArrow + case C.AKEYCODE_DPAD_LEFT: + n = key.NameLeftArrow + case C.AKEYCODE_DPAD_RIGHT: + n = key.NameRightArrow + case C.AKEYCODE_FORWARD_DEL: + n = key.NameDeleteForward + case C.AKEYCODE_DEL: + n = key.NameDeleteBackward + case C.AKEYCODE_NUMPAD_ENTER: + n = key.NameEnter + case C.AKEYCODE_ENTER: + n = key.NameEnter + default: + return "", false + } + return n, true +} + +//export Java_org_gioui_GioView_onKeyEvent +func Java_org_gioui_GioView_onKeyEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, keyCode, r C.jint, t C.jlong) { + w := views[handle] + if n, ok := convertKeyCode(keyCode); ok { + w.callbacks.Event(key.Event{Name: n}) + } + if r != 0 && r != '\n' { // Checking for "\n" to prevent duplication with key.NameEnter (gio#224). + w.callbacks.Event(key.EditEvent{Text: string(rune(r))}) + } +} + +//export Java_org_gioui_GioView_onTouchEvent +func Java_org_gioui_GioView_onTouchEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, action, pointerID, tool C.jint, x, y, scrollX, scrollY C.jfloat, jbtns C.jint, t C.jlong) { + w := views[handle] + var typ pointer.Type + switch action { + case C.AMOTION_EVENT_ACTION_DOWN, C.AMOTION_EVENT_ACTION_POINTER_DOWN: + typ = pointer.Press + case C.AMOTION_EVENT_ACTION_UP, C.AMOTION_EVENT_ACTION_POINTER_UP: + typ = pointer.Release + case C.AMOTION_EVENT_ACTION_CANCEL: + typ = pointer.Cancel + case C.AMOTION_EVENT_ACTION_MOVE: + typ = pointer.Move + case C.AMOTION_EVENT_ACTION_SCROLL: + typ = pointer.Scroll + default: + return + } + var src pointer.Source + var btns pointer.Buttons + if jbtns&C.AMOTION_EVENT_BUTTON_PRIMARY != 0 { + btns |= pointer.ButtonPrimary + } + if jbtns&C.AMOTION_EVENT_BUTTON_SECONDARY != 0 { + btns |= pointer.ButtonSecondary + } + if jbtns&C.AMOTION_EVENT_BUTTON_TERTIARY != 0 { + btns |= pointer.ButtonTertiary + } + switch tool { + case C.AMOTION_EVENT_TOOL_TYPE_FINGER: + src = pointer.Touch + case C.AMOTION_EVENT_TOOL_TYPE_STYLUS: + src = pointer.Touch + case C.AMOTION_EVENT_TOOL_TYPE_MOUSE: + src = pointer.Mouse + case C.AMOTION_EVENT_TOOL_TYPE_UNKNOWN: + // For example, triggered via 'adb shell input tap'. + // Instead of discarding it, treat it as a touch event. + src = pointer.Touch + default: + return + } + w.callbacks.Event(pointer.Event{ + Type: typ, + Source: src, + Buttons: btns, + PointerID: pointer.ID(pointerID), + Time: time.Duration(t) * time.Millisecond, + Position: f32.Point{X: float32(x), Y: float32(y)}, + Scroll: f32.Pt(float32(scrollX), float32(scrollY)), + }) +} + +func (w *window) ShowTextInput(show bool) { + runInJVM(javaVM(), func(env *C.JNIEnv) { + if show { + callVoidMethod(env, w.view, gioView.showTextInput) + } else { + callVoidMethod(env, w.view, gioView.hideTextInput) + } + }) +} + +func (w *window) SetInputHint(mode key.InputHint) { + // Constants defined at https://developer.android.com/reference/android/text/InputType. + const ( + TYPE_NULL = 0 + TYPE_CLASS_NUMBER = 2 + TYPE_NUMBER_FLAG_DECIMAL = 8192 + TYPE_NUMBER_FLAG_SIGNED = 4096 + TYPE_TEXT_FLAG_NO_SUGGESTIONS = 524288 + TYPE_TEXT_VARIATION_VISIBLE_PASSWORD = 144 + ) + + runInJVM(javaVM(), func(env *C.JNIEnv) { + var m jvalue + switch mode { + case key.HintNumeric: + m = TYPE_CLASS_NUMBER | TYPE_NUMBER_FLAG_DECIMAL | TYPE_NUMBER_FLAG_SIGNED + default: + // TYPE_NULL, since TYPE_CLASS_TEXT isn't currently supported. + m = TYPE_NULL + } + + // The TYPE_TEXT_FLAG_NO_SUGGESTIONS and TYPE_TEXT_VARIATION_VISIBLE_PASSWORD are used to fix the + // Samsung keyboard compatibility, forcing to disable the suggests/auto-complete. gio#116. + m = m | TYPE_TEXT_FLAG_NO_SUGGESTIONS | TYPE_TEXT_VARIATION_VISIBLE_PASSWORD + + callVoidMethod(env, w.view, gioView.setInputHint, m) + }) +} + +func javaBool(b bool) C.jboolean { + if b { + return C.JNI_TRUE + } else { + return C.JNI_FALSE + } +} + +func javaString(env *C.JNIEnv, str string) C.jstring { + if str == "" { + return 0 + } + utf16Chars := utf16.Encode([]rune(str)) + return C.jni_NewString(env, (*C.jchar)(unsafe.Pointer(&utf16Chars[0])), C.int(len(utf16Chars))) +} + +func varArgs(args []jvalue) *C.jvalue { + if len(args) == 0 { + return nil + } + return (*C.jvalue)(unsafe.Pointer(&args[0])) +} + +func callStaticVoidMethod(env *C.JNIEnv, cls C.jclass, method C.jmethodID, args ...jvalue) error { + C.jni_CallStaticVoidMethodA(env, cls, method, varArgs(args)) + return exception(env) +} + +func callStaticObjectMethod(env *C.JNIEnv, cls C.jclass, method C.jmethodID, args ...jvalue) (C.jobject, error) { + res := C.jni_CallStaticObjectMethodA(env, cls, method, varArgs(args)) + return res, exception(env) +} + +func callVoidMethod(env *C.JNIEnv, obj C.jobject, method C.jmethodID, args ...jvalue) error { + C.jni_CallVoidMethodA(env, obj, method, varArgs(args)) + return exception(env) +} + +func callBooleanMethod(env *C.JNIEnv, obj C.jobject, method C.jmethodID, args ...jvalue) (bool, error) { + res := C.jni_CallBooleanMethodA(env, obj, method, varArgs(args)) + return res == C.JNI_TRUE, exception(env) +} + +func callObjectMethod(env *C.JNIEnv, obj C.jobject, method C.jmethodID, args ...jvalue) (C.jobject, error) { + res := C.jni_CallObjectMethodA(env, obj, method, varArgs(args)) + return res, exception(env) +} + +func newObject(env *C.JNIEnv, cls C.jclass, method C.jmethodID, args ...jvalue) (C.jobject, error) { + res := C.jni_NewObjectA(env, cls, method, varArgs(args)) + return res, exception(env) +} + +// exception returns an error corresponding to the pending +// exception, or nil if no exception is pending. The pending +// exception is cleared. +func exception(env *C.JNIEnv) error { + thr := C.jni_ExceptionOccurred(env) + if thr == 0 { + return nil + } + C.jni_ExceptionClear(env) + cls := getObjectClass(env, C.jobject(thr)) + toString := getMethodID(env, cls, "toString", "()Ljava/lang/String;") + msg, err := callObjectMethod(env, C.jobject(thr), toString) + if err != nil { + return err + } + return errors.New(goString(env, C.jstring(msg))) +} + +func getObjectClass(env *C.JNIEnv, obj C.jobject) C.jclass { + if obj == 0 { + panic("null object") + } + cls := C.jni_GetObjectClass(env, C.jobject(obj)) + if err := exception(env); err != nil { + // GetObjectClass should never fail. + panic(err) + } + return cls +} + +// goString converts the JVM jstring to a Go string. +func goString(env *C.JNIEnv, str C.jstring) string { + if str == 0 { + return "" + } + strlen := C.jni_GetStringLength(env, C.jstring(str)) + chars := C.jni_GetStringChars(env, C.jstring(str)) + var utf16Chars []uint16 + hdr := (*reflect.SliceHeader)(unsafe.Pointer(&utf16Chars)) + hdr.Data = uintptr(unsafe.Pointer(chars)) + hdr.Cap = int(strlen) + hdr.Len = int(strlen) + utf8 := utf16.Decode(utf16Chars) + return string(utf8) +} + +func findClass(env *C.JNIEnv, name string) C.jclass { + cn := C.CString(name) + defer C.free(unsafe.Pointer(cn)) + return C.jni_FindClass(env, cn) +} + +func osMain() { +} + +func newWindow(window *callbacks, options []Option) error { + mainWindow.in <- windowAndConfig{window, options} + return <-mainWindow.errs +} + +func (w *window) WriteClipboard(s string) { + runInJVM(javaVM(), func(env *C.JNIEnv) { + jstr := javaString(env, s) + callStaticVoidMethod(env, android.gioCls, android.mwriteClipboard, + jvalue(android.appCtx), jvalue(jstr)) + }) +} + +func (w *window) ReadClipboard() { + runInJVM(javaVM(), func(env *C.JNIEnv) { + c, err := callStaticObjectMethod(env, android.gioCls, android.mreadClipboard, + jvalue(android.appCtx)) + if err != nil { + return + } + content := goString(env, C.jstring(c)) + w.callbacks.Event(clipboard.Event{Text: content}) + }) +} + +func (w *window) Configure(options []Option) { + runInJVM(javaVM(), func(env *C.JNIEnv) { + prev := w.config + cnf := w.config + cnf.apply(unit.Metric{}, options) + if prev.Orientation != cnf.Orientation { + w.config.Orientation = cnf.Orientation + setOrientation(env, w.view, cnf.Orientation) + } + if prev.NavigationColor != cnf.NavigationColor { + w.config.NavigationColor = cnf.NavigationColor + setNavigationColor(env, w.view, cnf.NavigationColor) + } + if prev.StatusColor != cnf.StatusColor { + w.config.StatusColor = cnf.StatusColor + setStatusColor(env, w.view, cnf.StatusColor) + } + if prev.Mode != cnf.Mode { + switch cnf.Mode { + case Fullscreen: + callVoidMethod(env, w.view, gioView.setFullscreen, C.JNI_TRUE) + w.config.Mode = Fullscreen + case Windowed: + callVoidMethod(env, w.view, gioView.setFullscreen, C.JNI_FALSE) + w.config.Mode = Windowed + } + } + if w.config != prev { + w.callbacks.Event(ConfigEvent{Config: w.config}) + } + }) +} + +func (w *window) Raise() {} + +func (w *window) SetCursor(name pointer.CursorName) { + runInJVM(javaVM(), func(env *C.JNIEnv) { + setCursor(env, w.view, name) + }) +} + +func (w *window) Wakeup() { + runOnMain(func(env *C.JNIEnv) { + w.callbacks.Event(wakeupEvent{}) + }) +} + +func setCursor(env *C.JNIEnv, view C.jobject, name pointer.CursorName) { + var curID int + switch name { + default: + fallthrough + case pointer.CursorDefault: + curID = 1000 // TYPE_ARROW + case pointer.CursorText: + curID = 1008 // TYPE_TEXT + case pointer.CursorPointer: + curID = 1002 // TYPE_HAND + case pointer.CursorCrossHair: + curID = 1007 // TYPE_CROSSHAIR + case pointer.CursorColResize: + curID = 1014 // TYPE_HORIZONTAL_DOUBLE_ARROW + case pointer.CursorRowResize: + curID = 1015 // TYPE_VERTICAL_DOUBLE_ARROW + case pointer.CursorNone: + curID = 0 // TYPE_NULL + } + callVoidMethod(env, view, gioView.setCursor, jvalue(curID)) +} + +func setOrientation(env *C.JNIEnv, view C.jobject, mode Orientation) { + var ( + id int + idFallback int // Used only for SDK 17 or older. + ) + // Constants defined at https://developer.android.com/reference/android/content/pm/ActivityInfo. + switch mode { + case AnyOrientation: + id, idFallback = 2, 2 // SCREEN_ORIENTATION_USER + case LandscapeOrientation: + id, idFallback = 11, 0 // SCREEN_ORIENTATION_USER_LANDSCAPE (or SCREEN_ORIENTATION_LANDSCAPE) + case PortraitOrientation: + id, idFallback = 12, 1 // SCREEN_ORIENTATION_USER_PORTRAIT (or SCREEN_ORIENTATION_PORTRAIT) + } + callVoidMethod(env, view, gioView.setOrientation, jvalue(id), jvalue(idFallback)) +} + +func setStatusColor(env *C.JNIEnv, view C.jobject, color color.NRGBA) { + callVoidMethod(env, view, gioView.setStatusColor, + jvalue(uint32(color.A)<<24|uint32(color.R)<<16|uint32(color.G)<<8|uint32(color.B)), + jvalue(int(f32color.LinearFromSRGB(color).Luminance()*255)), + ) +} + +func setNavigationColor(env *C.JNIEnv, view C.jobject, color color.NRGBA) { + callVoidMethod(env, view, gioView.setNavigationColor, + jvalue(uint32(color.A)<<24|uint32(color.R)<<16|uint32(color.G)<<8|uint32(color.B)), + jvalue(int(f32color.LinearFromSRGB(color).Luminance()*255)), + ) +} + +// Close the window. Not implemented for Android. +func (w *window) Close() {} + +// Maximize maximizes the window. Not implemented for Android. +func (w *window) Maximize() {} + +// Center the window. Not implemented for Android. +func (w *window) Center() {} + +// runOnMain runs a function on the Java main thread. +func runOnMain(f func(env *C.JNIEnv)) { + go func() { + mainFuncs <- f + runInJVM(javaVM(), func(env *C.JNIEnv) { + callStaticVoidMethod(env, android.gioCls, android.mwakeupMainThread) + }) + }() +} + +//export Java_org_gioui_Gio_scheduleMainFuncs +func Java_org_gioui_Gio_scheduleMainFuncs(env *C.JNIEnv, cls C.jclass) { + for { + select { + case f := <-mainFuncs: + f(env) + default: + return + } + } +} + +func (_ ViewEvent) ImplementsEvent() {} diff --git a/vendor/gioui.org/app/internal/window/os_darwin.go b/vendor/gioui.org/app/os_darwin.go index a3b77dc..c0554db 100644 --- a/vendor/gioui.org/app/internal/window/os_darwin.go +++ b/vendor/gioui.org/app/os_darwin.go @@ -1,18 +1,33 @@ // SPDX-License-Identifier: Unlicense OR MIT -package window +package app /* #include <Foundation/Foundation.h> __attribute__ ((visibility ("hidden"))) void gio_wakeupMainThread(void); -__attribute__ ((visibility ("hidden"))) NSUInteger gio_nsstringLength(CFTypeRef str); -__attribute__ ((visibility ("hidden"))) void gio_nsstringGetCharacters(CFTypeRef str, unichar *chars, NSUInteger loc, NSUInteger length); __attribute__ ((visibility ("hidden"))) CFTypeRef gio_createDisplayLink(void); __attribute__ ((visibility ("hidden"))) void gio_releaseDisplayLink(CFTypeRef dl); __attribute__ ((visibility ("hidden"))) int gio_startDisplayLink(CFTypeRef dl); __attribute__ ((visibility ("hidden"))) int gio_stopDisplayLink(CFTypeRef dl); __attribute__ ((visibility ("hidden"))) void gio_setDisplayLinkDisplay(CFTypeRef dl, uint64_t did); +__attribute__ ((visibility ("hidden"))) void gio_hideCursor(); +__attribute__ ((visibility ("hidden"))) void gio_showCursor(); +__attribute__ ((visibility ("hidden"))) void gio_setCursor(NSUInteger curID); + +static bool isMainThread() { + return [NSThread isMainThread]; +} + +static NSUInteger nsstringLength(CFTypeRef cstr) { + NSString *str = (__bridge NSString *)cstr; + return [str length]; +} + +static void nsstringGetCharacters(CFTypeRef cstr, unichar *chars, NSUInteger loc, NSUInteger length) { + NSString *str = (__bridge NSString *)cstr; + [str getCharacters:chars range:NSMakeRange(loc, length)]; +} */ import "C" import ( @@ -22,6 +37,8 @@ import ( "time" "unicode/utf16" "unsafe" + + "gioui.org/io/pointer" ) // displayLink is the state for a display link (CVDisplayLinkRef on macOS, @@ -50,6 +67,10 @@ var mainFuncs = make(chan func(), 1) // runOnMain runs the function on the main thread. func runOnMain(f func()) { + if C.isMainThread() { + f() + return + } go func() { mainFuncs <- f C.gio_wakeupMainThread() @@ -71,13 +92,16 @@ func gio_dispatchMainFuncs() { // nsstringToString converts a NSString to a Go string, and // releases the original string. func nsstringToString(str C.CFTypeRef) string { + if str == 0 { + return "" + } defer C.CFRelease(str) - n := C.gio_nsstringLength(str) + n := C.nsstringLength(str) if n == 0 { return "" } chars := make([]uint16, n) - C.gio_nsstringGetCharacters(str, (*C.unichar)(unsafe.Pointer(&chars[0])), 0, n) + C.nsstringGetCharacters(str, (*C.unichar)(unsafe.Pointer(&chars[0])), 0, n) utf8 := utf16.Decode(chars) return string(utf8) } @@ -169,3 +193,45 @@ func gio_onFrameCallback(dl C.CFTypeRef) { } } } + +// windowSetCursor updates the cursor from the current one to a new one +// and returns the new one. +func windowSetCursor(from, to pointer.CursorName) pointer.CursorName { + if from == to { + return to + } + var curID int + switch to { + default: + to = pointer.CursorDefault + fallthrough + case pointer.CursorDefault: + curID = 1 + case pointer.CursorText: + curID = 2 + case pointer.CursorPointer: + curID = 3 + case pointer.CursorCrossHair: + curID = 4 + case pointer.CursorColResize: + curID = 5 + case pointer.CursorRowResize: + curID = 6 + case pointer.CursorGrab: + curID = 7 + case pointer.CursorNone: + C.gio_hideCursor() + return to + } + if from == pointer.CursorNone { + C.gio_showCursor() + } + C.gio_setCursor(C.NSUInteger(curID)) + return to +} + +func (w *window) Wakeup() { + runOnMain(func() { + w.w.Event(wakeupEvent{}) + }) +} diff --git a/vendor/gioui.org/app/os_darwin.m b/vendor/gioui.org/app/os_darwin.m new file mode 100644 index 0000000..772cc28 --- /dev/null +++ b/vendor/gioui.org/app/os_darwin.m @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +@import Dispatch; +@import Foundation; + +#include "_cgo_export.h" + +void gio_wakeupMainThread(void) { + dispatch_async(dispatch_get_main_queue(), ^{ + gio_dispatchMainFuncs(); + }); +} diff --git a/vendor/gioui.org/app/internal/window/os_ios.go b/vendor/gioui.org/app/os_ios.go index 1865454..f6f6dea 100644 --- a/vendor/gioui.org/app/internal/window/os_ios.go +++ b/vendor/gioui.org/app/os_ios.go @@ -1,8 +1,9 @@ // SPDX-License-Identifier: Unlicense OR MIT +//go:build darwin && ios // +build darwin,ios -package window +package app /* #cgo CFLAGS: -DGLES_SILENCE_DEPRECATION -Werror -Wno-deprecated-declarations -fmodules -fobjc-arc -x objective-c @@ -17,14 +18,55 @@ struct drawParams { CGFloat top, right, bottom, left; }; -__attribute__ ((visibility ("hidden"))) void gio_showTextInput(CFTypeRef viewRef); -__attribute__ ((visibility ("hidden"))) void gio_hideTextInput(CFTypeRef viewRef); -__attribute__ ((visibility ("hidden"))) void gio_addLayerToView(CFTypeRef viewRef, CFTypeRef layerRef); -__attribute__ ((visibility ("hidden"))) void gio_updateView(CFTypeRef viewRef, CFTypeRef layerRef); -__attribute__ ((visibility ("hidden"))) void gio_removeLayer(CFTypeRef layerRef); -__attribute__ ((visibility ("hidden"))) struct drawParams gio_viewDrawParams(CFTypeRef viewRef); -__attribute__ ((visibility ("hidden"))) CFTypeRef gio_readClipboard(void); -__attribute__ ((visibility ("hidden"))) void gio_writeClipboard(unichar *chars, NSUInteger length); +static void writeClipboard(unichar *chars, NSUInteger length) { + @autoreleasepool { + NSString *s = [NSString string]; + if (length > 0) { + s = [NSString stringWithCharacters:chars length:length]; + } + UIPasteboard *p = UIPasteboard.generalPasteboard; + p.string = s; + } +} + +static CFTypeRef readClipboard(void) { + @autoreleasepool { + UIPasteboard *p = UIPasteboard.generalPasteboard; + return (__bridge_retained CFTypeRef)p.string; + } +} + +static void showTextInput(CFTypeRef viewRef) { + UIView *view = (__bridge UIView *)viewRef; + [view becomeFirstResponder]; +} + +static void hideTextInput(CFTypeRef viewRef) { + UIView *view = (__bridge UIView *)viewRef; + [view resignFirstResponder]; +} + +static struct drawParams viewDrawParams(CFTypeRef viewRef) { + UIView *v = (__bridge UIView *)viewRef; + struct drawParams params; + CGFloat scale = v.layer.contentsScale; + // Use 163 as the standard ppi on iOS. + params.dpi = 163*scale; + params.sdpi = params.dpi; + UIEdgeInsets insets = v.layoutMargins; + if (@available(iOS 11.0, tvOS 11.0, *)) { + UIFontMetrics *metrics = [UIFontMetrics defaultMetrics]; + params.sdpi = [metrics scaledValueForValue:params.sdpi]; + insets = v.safeAreaInsets; + } + params.width = v.bounds.size.width*scale; + params.height = v.bounds.size.height*scale; + params.top = insets.top*scale; + params.right = insets.right*scale; + params.bottom = insets.bottom*scale; + params.left = insets.left*scale; + return params; +} */ import "C" @@ -32,33 +74,36 @@ import ( "image" "runtime" "runtime/debug" - "sync/atomic" "time" "unicode/utf16" "unsafe" "gioui.org/f32" + "gioui.org/io/clipboard" "gioui.org/io/key" "gioui.org/io/pointer" "gioui.org/io/system" "gioui.org/unit" ) +type ViewEvent struct { + // ViewController is a CFTypeRef for the UIViewController backing a Window. + ViewController uintptr +} + type window struct { view C.CFTypeRef - w Callbacks + w *callbacks displayLink *displayLink - layer C.CFTypeRef - visible atomic.Value + visible bool + cursor pointer.CursorName pointerMap []C.CFTypeRef } var mainWindow = newWindowRendezvous() -var layerFactory func() uintptr - var views = make(map[C.CFTypeRef]*window) func init() { @@ -67,7 +112,7 @@ func init() { } //export onCreate -func onCreate(view C.CFTypeRef) { +func onCreate(view, controller C.CFTypeRef) { w := &window{ view: view, } @@ -81,11 +126,9 @@ func onCreate(view C.CFTypeRef) { wopts := <-mainWindow.out w.w = wopts.window w.w.SetDriver(w) - w.visible.Store(false) - w.layer = C.CFTypeRef(layerFactory()) - C.gio_addLayerToView(view, w.layer) views[view] = w w.w.Event(system.StageEvent{Stage: system.StagePaused}) + w.w.Event(ViewEvent{ViewController: uintptr(controller)}) } //export gio_onDraw @@ -95,18 +138,17 @@ func gio_onDraw(view C.CFTypeRef) { } func (w *window) draw(sync bool) { - params := C.gio_viewDrawParams(w.view) + params := C.viewDrawParams(w.view) if params.width == 0 || params.height == 0 { return } - wasVisible := w.isVisible() - w.visible.Store(true) - C.gio_updateView(w.view, w.layer) + wasVisible := w.visible + w.visible = true if !wasVisible { w.w.Event(system.StageEvent{Stage: system.StageRunning}) } const inchPrDp = 1.0 / 163 - w.w.Event(FrameEvent{ + w.w.Event(frameEvent{ FrameEvent: system.FrameEvent{ Now: time.Now(), Size: image.Point{ @@ -131,7 +173,7 @@ func (w *window) draw(sync bool) { //export onStop func onStop(view C.CFTypeRef) { w := views[view] - w.visible.Store(false) + w.visible = false w.w.Event(system.StageEvent{Stage: system.StagePaused}) } @@ -139,11 +181,9 @@ func onStop(view C.CFTypeRef) { func onDestroy(view C.CFTypeRef) { w := views[view] delete(views, view) + w.w.Event(ViewEvent{}) w.w.Event(system.DestroyEvent{}) w.displayLink.Close() - C.gio_removeLayer(w.layer) - C.CFRelease(w.layer) - w.layer = 0 w.view = 0 } @@ -220,23 +260,23 @@ func onTouch(last C.int, view, touchRef C.CFTypeRef, phase C.NSInteger, x, y C.C } func (w *window) ReadClipboard() { - runOnMain(func() { - content := nsstringToString(C.gio_readClipboard()) - w.w.Event(system.ClipboardEvent{Text: content}) - }) + content := nsstringToString(C.readClipboard()) + w.w.Event(clipboard.Event{Text: content}) } func (w *window) WriteClipboard(s string) { u16 := utf16.Encode([]rune(s)) - runOnMain(func() { - var chars *C.unichar - if len(u16) > 0 { - chars = (*C.unichar)(unsafe.Pointer(&u16[0])) - } - C.gio_writeClipboard(chars, C.NSUInteger(len(u16))) - }) + var chars *C.unichar + if len(u16) > 0 { + chars = (*C.unichar)(unsafe.Pointer(&u16[0])) + } + C.writeClipboard(chars, C.NSUInteger(len(u16))) } +func (w *window) Configure([]Option) {} + +func (w *window) Raise() {} + func (w *window) SetAnimating(anim bool) { v := w.view if v == 0 { @@ -249,6 +289,10 @@ func (w *window) SetAnimating(anim bool) { } } +func (w *window) SetCursor(name pointer.CursorName) { + w.cursor = windowSetCursor(w.cursor, name) +} + func (w *window) onKeyCommand(name string) { w.w.Event(key.Event{ Name: name, @@ -275,42 +319,40 @@ func (w *window) lookupTouch(last bool, touch C.CFTypeRef) pointer.ID { return pointer.ID(id) } -func (w *window) contextLayer() uintptr { - return uintptr(w.layer) -} - -func (w *window) isVisible() bool { - return w.visible.Load().(bool) +func (w *window) contextView() C.CFTypeRef { + return w.view } func (w *window) ShowTextInput(show bool) { - v := w.view - if v == 0 { - return + if show { + C.showTextInput(w.view) + } else { + C.hideTextInput(w.view) } - C.CFRetain(v) - runOnMain(func() { - defer C.CFRelease(v) - if show { - C.gio_showTextInput(w.view) - } else { - C.gio_hideTextInput(w.view) - } - }) } +func (w *window) SetInputHint(_ key.InputHint) {} + // Close the window. Not implemented for iOS. func (w *window) Close() {} -func NewWindow(win Callbacks, opts *Options) error { - mainWindow.in <- windowAndOptions{win, opts} +// Maximize the window. Not implemented for iOS. +func (w *window) Maximize() {} + +// Center the window. Not implemented for iOS. +func (w *window) Center() {} + +func newWindow(win *callbacks, options []Option) error { + mainWindow.in <- windowAndConfig{win, options} return <-mainWindow.errs } -func Main() { +func osMain() { } //export gio_runMain func gio_runMain() { runMain() } + +func (_ ViewEvent) ImplementsEvent() {} diff --git a/vendor/gioui.org/app/internal/window/os_ios.m b/vendor/gioui.org/app/os_ios.m index 66830e4..94f254b 100644 --- a/vendor/gioui.org/app/internal/window/os_ios.m +++ b/vendor/gioui.org/app/os_ios.m @@ -8,6 +8,8 @@ #include "_cgo_export.h" #include "framework_ios.h" +__attribute__ ((visibility ("hidden"))) Class gio_layerClass(void); + @interface GioView: UIView <UIKeyInput> @end @@ -28,7 +30,7 @@ CGFloat _keyboardHeight; #endif drawView.preservesSuperviewLayoutMargins = YES; drawView.layoutMargins = UIEdgeInsetsMake(0, 0, 0, 0); - onCreate((__bridge CFTypeRef)drawView); + onCreate((__bridge CFTypeRef)drawView, (__bridge CFTypeRef)self); [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChange:) name:UIKeyboardWillShowNotification @@ -125,6 +127,9 @@ NSArray<UIKeyCommand *> *_keyCommands; gio_onFrameCallback((__bridge CFTypeRef)link); } ++ (Class)layerClass { + return gio_layerClass(); +} - (void)willMoveToWindow:(UIWindow *)newWindow { if (self.window != nil) { [[NSNotificationCenter defaultCenter] removeObserver:self @@ -157,9 +162,6 @@ NSArray<UIKeyCommand *> *_keyCommands; } } -- (void)dealloc { -} - - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { handleTouches(0, self, touches, event); } @@ -229,74 +231,6 @@ NSArray<UIKeyCommand *> *_keyCommands; } @end -void gio_writeClipboard(unichar *chars, NSUInteger length) { - @autoreleasepool { - NSString *s = [NSString string]; - if (length > 0) { - s = [NSString stringWithCharacters:chars length:length]; - } - UIPasteboard *p = UIPasteboard.generalPasteboard; - p.string = s; - } -} - -CFTypeRef gio_readClipboard(void) { - @autoreleasepool { - UIPasteboard *p = UIPasteboard.generalPasteboard; - return (__bridge_retained CFTypeRef)p.string; - } -} - -void gio_showTextInput(CFTypeRef viewRef) { - UIView *view = (__bridge UIView *)viewRef; - [view becomeFirstResponder]; -} - -void gio_hideTextInput(CFTypeRef viewRef) { - UIView *view = (__bridge UIView *)viewRef; - [view resignFirstResponder]; -} - -void gio_addLayerToView(CFTypeRef viewRef, CFTypeRef layerRef) { - UIView *view = (__bridge UIView *)viewRef; - CALayer *layer = (__bridge CALayer *)layerRef; - [view.layer addSublayer:layer]; -} - -void gio_updateView(CFTypeRef viewRef, CFTypeRef layerRef) { - UIView *view = (__bridge UIView *)viewRef; - CAEAGLLayer *layer = (__bridge CAEAGLLayer *)layerRef; - layer.contentsScale = view.contentScaleFactor; - layer.bounds = view.bounds; -} - -void gio_removeLayer(CFTypeRef layerRef) { - CALayer *layer = (__bridge CALayer *)layerRef; - [layer removeFromSuperlayer]; -} - -struct drawParams gio_viewDrawParams(CFTypeRef viewRef) { - UIView *v = (__bridge UIView *)viewRef; - struct drawParams params; - CGFloat scale = v.layer.contentsScale; - // Use 163 as the standard ppi on iOS. - params.dpi = 163*scale; - params.sdpi = params.dpi; - UIEdgeInsets insets = v.layoutMargins; - if (@available(iOS 11.0, tvOS 11.0, *)) { - UIFontMetrics *metrics = [UIFontMetrics defaultMetrics]; - params.sdpi = [metrics scaledValueForValue:params.sdpi]; - insets = v.safeAreaInsets; - } - params.width = v.bounds.size.width*scale; - params.height = v.bounds.size.height*scale; - params.top = insets.top*scale; - params.right = insets.right*scale; - params.bottom = insets.bottom*scale; - params.left = insets.left*scale; - return params; -} - CFTypeRef gio_createDisplayLink(void) { CADisplayLink *dl = [CADisplayLink displayLinkWithTarget:[GioView class] selector:@selector(onFrameCallback:)]; dl.paused = YES; @@ -326,3 +260,15 @@ void gio_releaseDisplayLink(CFTypeRef dlref) { void gio_setDisplayLinkDisplay(CFTypeRef dl, uint64_t did) { // Nothing to do on iOS. } + +void gio_hideCursor() { + // Not supported. +} + +void gio_showCursor() { + // Not supported. +} + +void gio_setCursor(NSUInteger curID) { + // Not supported. +} diff --git a/vendor/gioui.org/app/internal/window/os_js.go b/vendor/gioui.org/app/os_js.go index 32235df..5b1388c 100644 --- a/vendor/gioui.org/app/internal/window/os_js.go +++ b/vendor/gioui.org/app/os_js.go @@ -1,42 +1,62 @@ // SPDX-License-Identifier: Unlicense OR MIT -package window +package app import ( + "fmt" "image" + "image/color" "strings" - "sync" "syscall/js" "time" "unicode" "unicode/utf8" + "gioui.org/internal/f32color" + "gioui.org/f32" + "gioui.org/io/clipboard" "gioui.org/io/key" "gioui.org/io/pointer" "gioui.org/io/system" "gioui.org/unit" ) +type ViewEvent struct{} + type window struct { window js.Value + document js.Value + head js.Value clipboard js.Value cnv js.Value tarea js.Value - w Callbacks + w *callbacks redraw js.Func clipboardCallback js.Func requestAnimationFrame js.Value + browserHistory js.Value + visualViewport js.Value + screenOrientation js.Value cleanfuncs []func() touches []js.Value composing bool + requestFocus bool + + chanAnimation chan struct{} + chanRedraw chan struct{} - mu sync.Mutex + config Config + inset f32.Point scale float32 animating bool + // animRequested tracks whether a requestAnimationFrame callback + // is pending. + animRequested bool + wakeups chan struct{} } -func NewWindow(win Callbacks, opts *Options) error { +func newWindow(win *callbacks, options []Option) error { doc := js.Global().Get("document") cont := getContainer(doc) cnv := createCanvas(doc) @@ -45,29 +65,55 @@ func NewWindow(win Callbacks, opts *Options) error { cont.Call("appendChild", tarea) w := &window{ cnv: cnv, + document: doc, tarea: tarea, window: js.Global().Get("window"), + head: doc.Get("head"), clipboard: js.Global().Get("navigator").Get("clipboard"), + wakeups: make(chan struct{}, 1), } w.requestAnimationFrame = w.window.Get("requestAnimationFrame") + w.browserHistory = w.window.Get("history") + w.visualViewport = w.window.Get("visualViewport") + if w.visualViewport.IsUndefined() { + w.visualViewport = w.window + } + if screen := w.window.Get("screen"); screen.Truthy() { + w.screenOrientation = screen.Get("orientation") + } + w.chanAnimation = make(chan struct{}, 1) + w.chanRedraw = make(chan struct{}, 1) w.redraw = w.funcOf(func(this js.Value, args []js.Value) interface{} { - w.animCallback() + w.chanAnimation <- struct{}{} return nil }) w.clipboardCallback = w.funcOf(func(this js.Value, args []js.Value) interface{} { content := args[0].String() - win.Event(system.ClipboardEvent{Text: content}) + go win.Event(clipboard.Event{Text: content}) return nil }) w.addEventListeners() + w.addHistory() w.w = win + go func() { + defer w.cleanup() w.w.SetDriver(w) - w.focus() + w.Configure(options) + w.blur() w.w.Event(system.StageEvent{Stage: system.StageRunning}) + w.resize() w.draw(true) - select {} - w.cleanup() + for { + select { + case <-w.wakeups: + w.w.Event(wakeupEvent{}) + case <-w.chanAnimation: + w.animCallback() + case <-w.chanRedraw: + w.draw(true) + } + } }() return nil } @@ -116,8 +162,33 @@ func (w *window) cleanup() { } func (w *window) addEventListeners() { - w.addEventListener(w.window, "resize", func(this js.Value, args []js.Value) interface{} { - w.draw(true) + w.addEventListener(w.visualViewport, "resize", func(this js.Value, args []js.Value) interface{} { + w.resize() + w.chanRedraw <- struct{}{} + return nil + }) + w.addEventListener(w.window, "contextmenu", func(this js.Value, args []js.Value) interface{} { + args[0].Call("preventDefault") + return nil + }) + w.addEventListener(w.window, "popstate", func(this js.Value, args []js.Value) interface{} { + ev := &system.CommandEvent{Type: system.CommandBack} + w.w.Event(ev) + if ev.Cancel { + return w.browserHistory.Call("forward") + } + + return w.browserHistory.Call("back") + }) + w.addEventListener(w.document, "visibilitychange", func(this js.Value, args []js.Value) interface{} { + ev := system.StageEvent{} + switch w.document.Get("visibilityState").String() { + case "hidden", "prerender", "unloaded": + ev.Stage = system.StagePaused + default: + ev.Stage = system.StageRunning + } + w.w.Event(ev) return nil }) w.addEventListener(w.cnv, "mousemove", func(this js.Value, args []js.Value) interface{} { @@ -126,6 +197,10 @@ func (w *window) addEventListeners() { }) w.addEventListener(w.cnv, "mousedown", func(this js.Value, args []js.Value) interface{} { w.pointerEvent(pointer.Press, 0, 0, args[0]) + if w.requestFocus { + w.focus() + w.requestFocus = false + } return nil }) w.addEventListener(w.cnv, "mouseup", func(this js.Value, args []js.Value) interface{} { @@ -149,6 +224,10 @@ func (w *window) addEventListeners() { }) w.addEventListener(w.cnv, "touchstart", func(this js.Value, args []js.Value) interface{} { w.touchEvent(pointer.Press, args[0]) + if w.requestFocus { + w.focus() // iOS can only focus inside a Touch event. + w.requestFocus = false + } return nil }) w.addEventListener(w.cnv, "touchend", func(this js.Value, args []js.Value) interface{} { @@ -177,10 +256,15 @@ func (w *window) addEventListeners() { }) w.addEventListener(w.tarea, "blur", func(this js.Value, args []js.Value) interface{} { w.w.Event(key.FocusEvent{Focus: false}) + w.blur() return nil }) w.addEventListener(w.tarea, "keydown", func(this js.Value, args []js.Value) interface{} { - w.keyEvent(args[0]) + w.keyEvent(args[0], key.Press) + return nil + }) + w.addEventListener(w.tarea, "keyup", func(this js.Value, args []js.Value) interface{} { + w.keyEvent(args[0], key.Release) return nil }) w.addEventListener(w.tarea, "compositionstart", func(this js.Value, args []js.Value) interface{} { @@ -199,6 +283,18 @@ func (w *window) addEventListeners() { w.flushInput() return nil }) + w.addEventListener(w.tarea, "paste", func(this js.Value, args []js.Value) interface{} { + if w.clipboard.IsUndefined() { + return nil + } + // Prevents duplicated-paste, since "paste" is already handled through Clipboard API. + args[0].Call("preventDefault") + return nil + }) +} + +func (w *window) addHistory() { + w.browserHistory.Call("pushState", nil, nil, w.window.Get("location").Get("href")) } func (w *window) flushInput() { @@ -209,18 +305,42 @@ func (w *window) flushInput() { func (w *window) blur() { w.tarea.Call("blur") + w.requestFocus = false } func (w *window) focus() { w.tarea.Call("focus") + w.requestFocus = true +} + +func (w *window) keyboard(hint key.InputHint) { + var m string + switch hint { + case key.HintAny: + m = "text" + case key.HintText: + m = "text" + case key.HintNumeric: + m = "decimal" + case key.HintEmail: + m = "email" + case key.HintURL: + m = "url" + case key.HintTelephone: + m = "tel" + default: + m = "text" + } + w.tarea.Set("inputMode", m) } -func (w *window) keyEvent(e js.Value) { +func (w *window) keyEvent(e js.Value, ks key.State) { k := e.Get("key").String() if n, ok := translateKey(k); ok { cmd := key.Event{ Name: n, Modifiers: modifiersFor(e), + State: ks, } w.w.Event(cmd) } @@ -230,6 +350,10 @@ func (w *window) keyEvent(e js.Value) { // KeyEvent. func modifiersFor(e js.Value) key.Modifiers { var mods key.Modifiers + if e.Get("getModifierState").IsUndefined() { + // Some browsers doesn't support getModifierState. + return mods + } if e.Call("getModifierState", "Alt").Bool() { mods |= key.ModAlt } @@ -248,9 +372,7 @@ func (w *window) touchEvent(typ pointer.Type, e js.Value) { changedTouches := e.Get("changedTouches") n := changedTouches.Length() rect := w.cnv.Call("getBoundingClientRect") - w.mu.Lock() scale := w.scale - w.mu.Unlock() var mods key.Modifiers if e.Get("shiftKey").Bool() { mods |= key.ModShift @@ -277,7 +399,7 @@ func (w *window) touchEvent(typ pointer.Type, e js.Value) { Position: pos, PointerID: pid, Time: t, - Modifiers: modifiersFor(e), + Modifiers: mods, }) } } @@ -300,9 +422,7 @@ func (w *window) pointerEvent(typ pointer.Type, dx, dy float32, e js.Value) { rect := w.cnv.Call("getBoundingClientRect") x -= rect.Get("left").Float() y -= rect.Get("top").Float() - w.mu.Lock() scale := w.scale - w.mu.Unlock() pos := f32.Point{ X: float32(x) * scale, Y: float32(y) * scale, @@ -315,13 +435,13 @@ func (w *window) pointerEvent(typ pointer.Type, dx, dy float32, e js.Value) { jbtns := e.Get("buttons").Int() var btns pointer.Buttons if jbtns&1 != 0 { - btns |= pointer.ButtonLeft + btns |= pointer.ButtonPrimary } if jbtns&2 != 0 { - btns |= pointer.ButtonRight + btns |= pointer.ButtonSecondary } if jbtns&4 != 0 { - btns |= pointer.ButtonMiddle + btns |= pointer.ButtonTertiary } w.w.Event(pointer.Event{ Type: typ, @@ -343,7 +463,7 @@ func (w *window) addEventListener(this js.Value, event string, f func(this js.Va } // funcOf is like js.FuncOf but adds the js.Func to a list of -// functions to be released up. +// functions to be released during cleanup. func (w *window) funcOf(f func(this js.Value, args []js.Value) interface{}) js.Func { jsf := js.FuncOf(f) w.cleanfuncs = append(w.cleanfuncs, jsf.Release) @@ -351,24 +471,22 @@ func (w *window) funcOf(f func(this js.Value, args []js.Value) interface{}) js.F } func (w *window) animCallback() { - w.mu.Lock() anim := w.animating + w.animRequested = anim if anim { w.requestAnimationFrame.Invoke(w.redraw) } - w.mu.Unlock() if anim { w.draw(false) } } func (w *window) SetAnimating(anim bool) { - w.mu.Lock() - defer w.mu.Unlock() - if anim && !w.animating { + w.animating = anim + if anim && !w.animRequested { + w.animRequested = true w.requestAnimationFrame.Invoke(w.redraw) } - w.animating = anim } func (w *window) ReadClipboard() { @@ -391,6 +509,44 @@ func (w *window) WriteClipboard(s string) { w.clipboard.Call("writeText", s) } +func (w *window) Configure(options []Option) { + prev := w.config + cnf := w.config + cnf.apply(unit.Metric{}, options) + if prev.Title != cnf.Title { + w.config.Title = cnf.Title + w.document.Set("title", cnf.Title) + } + if prev.Mode != cnf.Mode { + w.windowMode(cnf.Mode) + } + if prev.NavigationColor != cnf.NavigationColor { + w.config.NavigationColor = cnf.NavigationColor + w.navigationColor(cnf.NavigationColor) + } + if prev.Orientation != cnf.Orientation { + w.config.Orientation = cnf.Orientation + w.orientation(cnf.Orientation) + } + if w.config != prev { + w.w.Event(ConfigEvent{Config: w.config}) + } +} + +func (w *window) Raise() {} + +func (w *window) SetCursor(name pointer.CursorName) { + style := w.cnv.Get("style") + style.Set("cursor", string(name)) +} + +func (w *window) Wakeup() { + select { + case w.wakeups <- struct{}{}: + default: + } +} + func (w *window) ShowTextInput(show bool) { // Run in a goroutine to avoid a deadlock if the // focus change result in an event. @@ -403,49 +559,119 @@ func (w *window) ShowTextInput(show bool) { }() } +func (w *window) SetInputHint(mode key.InputHint) { + w.keyboard(mode) +} + // Close the window. Not implemented for js. func (w *window) Close() {} +// Maximize the window. Not implemented for js. +func (w *window) Maximize() {} + +// Center the window. Not implemented for js. +func (w *window) Center() {} + +func (w *window) resize() { + w.scale = float32(w.window.Get("devicePixelRatio").Float()) + + rect := w.cnv.Call("getBoundingClientRect") + size := image.Point{ + X: int(float32(rect.Get("width").Float()) * w.scale), + Y: int(float32(rect.Get("height").Float()) * w.scale), + } + if size != w.config.Size { + w.config.Size = size + w.w.Event(ConfigEvent{Config: w.config}) + } + + if vx, vy := w.visualViewport.Get("width"), w.visualViewport.Get("height"); !vx.IsUndefined() && !vy.IsUndefined() { + w.inset.X = float32(w.config.Size.X) - float32(vx.Float())*w.scale + w.inset.Y = float32(w.config.Size.Y) - float32(vy.Float())*w.scale + } + + if w.config.Size.X == 0 || w.config.Size.Y == 0 { + return + } + + w.cnv.Set("width", w.config.Size.X) + w.cnv.Set("height", w.config.Size.Y) +} + func (w *window) draw(sync bool) { - width, height, scale, cfg := w.config() - if cfg == (unit.Metric{}) || width == 0 || height == 0 { + size, insets, metric := w.getConfig() + if metric == (unit.Metric{}) || size.X == 0 || size.Y == 0 { return } - w.mu.Lock() - w.scale = float32(scale) - w.mu.Unlock() - w.w.Event(FrameEvent{ + w.w.Event(frameEvent{ FrameEvent: system.FrameEvent{ - Now: time.Now(), - Size: image.Point{ - X: width, - Y: height, - }, - Metric: cfg, + Now: time.Now(), + Size: size, + Insets: insets, + Metric: metric, }, Sync: sync, }) } -func (w *window) config() (int, int, float32, unit.Metric) { - rect := w.cnv.Call("getBoundingClientRect") - width, height := rect.Get("width").Float(), rect.Get("height").Float() - scale := w.window.Get("devicePixelRatio").Float() - width *= scale - height *= scale - iw, ih := int(width+.5), int(height+.5) - // Adjust internal size of canvas if necessary. - if cw, ch := w.cnv.Get("width").Int(), w.cnv.Get("height").Int(); iw != cw || ih != ch { - w.cnv.Set("width", iw) - w.cnv.Set("height", ih) +func (w *window) getConfig() (image.Point, system.Insets, unit.Metric) { + return image.Pt(w.config.Size.X, w.config.Size.Y), system.Insets{ + Bottom: unit.Px(w.inset.Y), + Right: unit.Px(w.inset.X), + }, unit.Metric{ + PxPerDp: w.scale, + PxPerSp: w.scale, + } +} + +func (w *window) windowMode(mode WindowMode) { + switch mode { + case Windowed: + if !w.document.Get("fullscreenElement").Truthy() { + return // Browser is already Windowed. + } + if !w.document.Get("exitFullscreen").Truthy() { + return // Browser doesn't support such feature. + } + w.document.Call("exitFullscreen") + w.config.Mode = Windowed + case Fullscreen: + elem := w.document.Get("documentElement") + if !elem.Get("requestFullscreen").Truthy() { + return // Browser doesn't support such feature. + } + elem.Call("requestFullscreen") + w.config.Mode = Fullscreen + } +} + +func (w *window) orientation(mode Orientation) { + if j := w.screenOrientation; !j.Truthy() || !j.Get("unlock").Truthy() || !j.Get("lock").Truthy() { + return // Browser don't support Screen Orientation API. } - return iw, ih, float32(scale), unit.Metric{ - PxPerDp: float32(scale), - PxPerSp: float32(scale), + + switch mode { + case AnyOrientation: + w.screenOrientation.Call("unlock") + case LandscapeOrientation: + w.screenOrientation.Call("lock", "landscape").Call("then", w.redraw) + case PortraitOrientation: + w.screenOrientation.Call("lock", "portrait").Call("then", w.redraw) } } -func Main() { +func (w *window) navigationColor(c color.NRGBA) { + theme := w.head.Call("querySelector", `meta[name="theme-color"]`) + if !theme.Truthy() { + theme = w.document.Call("createElement", "meta") + theme.Set("name", "theme-color") + w.head.Call("appendChild", theme) + } + rgba := f32color.NRGBAToRGBA(c) + theme.Set("content", fmt.Sprintf("#%06X", []uint8{rgba.R, rgba.G, rgba.B})) +} + +func osMain() { select {} } @@ -479,7 +705,7 @@ func translateKey(k string) (string, bool) { case "Tab": n = key.NameTab case " ": - n = "Space" + n = key.NameSpace case "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12": n = k default: @@ -492,3 +718,5 @@ func translateKey(k string) (string, bool) { } return n, true } + +func (_ ViewEvent) ImplementsEvent() {} diff --git a/vendor/gioui.org/app/os_macos.go b/vendor/gioui.org/app/os_macos.go new file mode 100644 index 0000000..270db6a --- /dev/null +++ b/vendor/gioui.org/app/os_macos.go @@ -0,0 +1,663 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build darwin && !ios +// +build darwin,!ios + +package app + +import ( + "errors" + "image" + "runtime" + "time" + "unicode" + "unicode/utf16" + "unsafe" + + "gioui.org/f32" + "gioui.org/io/clipboard" + "gioui.org/io/key" + "gioui.org/io/pointer" + "gioui.org/io/system" + "gioui.org/unit" + + _ "gioui.org/internal/cocoainit" +) + +/* +#cgo CFLAGS: -DGL_SILENCE_DEPRECATION -Werror -Wno-deprecated-declarations -fmodules -fobjc-arc -x objective-c + +#include <AppKit/AppKit.h> + +#define MOUSE_MOVE 1 +#define MOUSE_UP 2 +#define MOUSE_DOWN 3 +#define MOUSE_SCROLL 4 + +__attribute__ ((visibility ("hidden"))) void gio_main(void); +__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createView(void); +__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight); + +static void writeClipboard(unichar *chars, NSUInteger length) { + @autoreleasepool { + NSString *s = [NSString string]; + if (length > 0) { + s = [NSString stringWithCharacters:chars length:length]; + } + NSPasteboard *p = NSPasteboard.generalPasteboard; + [p declareTypes:@[NSPasteboardTypeString] owner:nil]; + [p setString:s forType:NSPasteboardTypeString]; + } +} + +static CFTypeRef readClipboard(void) { + @autoreleasepool { + NSPasteboard *p = NSPasteboard.generalPasteboard; + NSString *content = [p stringForType:NSPasteboardTypeString]; + return (__bridge_retained CFTypeRef)content; + } +} + +static CGFloat viewHeight(CFTypeRef viewRef) { + NSView *view = (__bridge NSView *)viewRef; + return [view bounds].size.height; +} + +static CGFloat viewWidth(CFTypeRef viewRef) { + NSView *view = (__bridge NSView *)viewRef; + return [view bounds].size.width; +} + +static CGFloat getScreenBackingScale(void) { + return [NSScreen.mainScreen backingScaleFactor]; +} + +static CGFloat getViewBackingScale(CFTypeRef viewRef) { + NSView *view = (__bridge NSView *)viewRef; + return [view.window backingScaleFactor]; +} + +static void setNeedsDisplay(CFTypeRef viewRef) { + NSView *view = (__bridge NSView *)viewRef; + [view setNeedsDisplay:YES]; +} + +static NSPoint cascadeTopLeftFromPoint(CFTypeRef windowRef, NSPoint topLeft) { + NSWindow *window = (__bridge NSWindow *)windowRef; + return [window cascadeTopLeftFromPoint:topLeft]; +} + +static void makeKeyAndOrderFront(CFTypeRef windowRef) { + NSWindow *window = (__bridge NSWindow *)windowRef; + [window makeKeyAndOrderFront:nil]; +} + +static void toggleFullScreen(CFTypeRef windowRef) { + NSWindow *window = (__bridge NSWindow *)windowRef; + [window toggleFullScreen:nil]; +} + +static NSWindowStyleMask getWindowStyleMask(CFTypeRef windowRef) { + NSWindow *window = (__bridge NSWindow *)windowRef; + return [window styleMask]; +} + +static void closeWindow(CFTypeRef windowRef) { + NSWindow* window = (__bridge NSWindow *)windowRef; + [window performClose:nil]; +} + +static void setSize(CFTypeRef windowRef, CGFloat width, CGFloat height) { + NSWindow* window = (__bridge NSWindow *)windowRef; + NSSize size = NSMakeSize(width, height); + [window setContentSize:size]; +} + +static void setMinSize(CFTypeRef windowRef, CGFloat width, CGFloat height) { + NSWindow* window = (__bridge NSWindow *)windowRef; + window.contentMinSize = NSMakeSize(width, height); +} + +static void setMaxSize(CFTypeRef windowRef, CGFloat width, CGFloat height) { + NSWindow* window = (__bridge NSWindow *)windowRef; + window.contentMaxSize = NSMakeSize(width, height); +} + +static void setScreenFrame(CFTypeRef windowRef, CGFloat x, CGFloat y, CGFloat w, CGFloat h) { + NSWindow* window = (__bridge NSWindow *)windowRef; + NSRect r = NSMakeRect(x, y, w, h); + [window setFrame:r display:YES]; +} + +static NSRect getScreenFrame(CFTypeRef windowRef) { + NSWindow* window = (__bridge NSWindow *)windowRef; + return [[window screen] frame]; +} + +static void setTitle(CFTypeRef windowRef, const char *title) { + NSWindow* window = (__bridge NSWindow *)windowRef; + window.title = [NSString stringWithUTF8String: title]; +} + +static CFTypeRef layerForView(CFTypeRef viewRef) { + NSView *view = (__bridge NSView *)viewRef; + return (__bridge CFTypeRef)view.layer; +} + +static void raiseWindow(CFTypeRef windowRef) { + NSWindow* window = (__bridge NSWindow *)windowRef; + [window makeKeyAndOrderFront:nil]; +} + +*/ +import "C" + +func init() { + // Darwin requires that UI operations happen on the main thread only. + runtime.LockOSThread() +} + +// ViewEvent notified the client of changes to the window AppKit handles. +// The handles are retained until another ViewEvent is sent. +type ViewEvent struct { + // View is a CFTypeRef for the NSView for the window. + View uintptr + // Layer is a CFTypeRef of the CALayer of View. + Layer uintptr +} + +type window struct { + view C.CFTypeRef + window C.CFTypeRef + w *callbacks + stage system.Stage + displayLink *displayLink + cursor pointer.CursorName + + scale float32 + config Config +} + +// viewMap is the mapping from Cocoa NSViews to Go windows. +var viewMap = make(map[C.CFTypeRef]*window) + +// launched is closed when applicationDidFinishLaunching is called. +var launched = make(chan struct{}) + +// nextTopLeft is the offset to use for the next window's call to +// cascadeTopLeftFromPoint. +var nextTopLeft C.NSPoint + +// mustView is like lookupView, except that it panics +// if the view isn't mapped. +func mustView(view C.CFTypeRef) *window { + w, ok := lookupView(view) + if !ok { + panic("no window for view") + } + return w +} + +func lookupView(view C.CFTypeRef) (*window, bool) { + w, exists := viewMap[view] + if !exists { + return nil, false + } + return w, true +} + +func deleteView(view C.CFTypeRef) { + delete(viewMap, view) +} + +func insertView(view C.CFTypeRef, w *window) { + viewMap[view] = w +} + +func (w *window) contextView() C.CFTypeRef { + return w.view +} + +func (w *window) ReadClipboard() { + content := nsstringToString(C.readClipboard()) + w.w.Event(clipboard.Event{Text: content}) +} + +func (w *window) WriteClipboard(s string) { + u16 := utf16.Encode([]rune(s)) + var chars *C.unichar + if len(u16) > 0 { + chars = (*C.unichar)(unsafe.Pointer(&u16[0])) + } + C.writeClipboard(chars, C.NSUInteger(len(u16))) +} + +func (w *window) updateWindowMode() { + style := int(C.getWindowStyleMask(w.window)) + if style&C.NSWindowStyleMaskFullScreen > 0 { + w.config.Mode = Fullscreen + } else { + w.config.Mode = Windowed + } +} + +func (w *window) Configure(options []Option) { + screenScale := float32(C.getScreenBackingScale()) + cfg := configFor(screenScale) + prev := w.config + w.updateWindowMode() + cnf := w.config + cnf.apply(cfg, options) + cnf.Size = cnf.Size.Div(int(screenScale)) + cnf.MinSize = cnf.MinSize.Div(int(screenScale)) + cnf.MaxSize = cnf.MaxSize.Div(int(screenScale)) + + if cnf.Mode != Fullscreen && prev.Size != cnf.Size { + w.config.Size = cnf.Size + C.setSize(w.window, C.CGFloat(cnf.Size.X), C.CGFloat(cnf.Size.Y)) + } + if prev.MinSize != cnf.MinSize { + w.config.MinSize = cnf.MinSize + C.setMinSize(w.window, C.CGFloat(cnf.MinSize.X), C.CGFloat(cnf.MinSize.Y)) + } + if prev.MaxSize != cnf.MaxSize { + w.config.MaxSize = cnf.MaxSize + C.setMaxSize(w.window, C.CGFloat(cnf.MaxSize.X), C.CGFloat(cnf.MaxSize.Y)) + } + + if prev.Title != cnf.Title { + w.config.Title = cnf.Title + title := C.CString(cnf.Title) + defer C.free(unsafe.Pointer(title)) + C.setTitle(w.window, title) + } + if prev.Mode != cnf.Mode { + switch cnf.Mode { + case Windowed, Fullscreen: + w.config.Mode = cnf.Mode + C.toggleFullScreen(w.window) + } + } + if w.config != prev { + w.w.Event(ConfigEvent{Config: w.config}) + } +} + +func (w *window) SetCursor(name pointer.CursorName) { + w.cursor = windowSetCursor(w.cursor, name) +} + +func (w *window) ShowTextInput(show bool) {} + +func (w *window) SetInputHint(_ key.InputHint) {} + +func (w *window) SetAnimating(anim bool) { + if anim { + w.displayLink.Start() + } else { + w.displayLink.Stop() + } +} + +func (w *window) Raise() { + C.raiseWindow(w.window) +} + +func (w *window) runOnMain(f func()) { + runOnMain(func() { + // Make sure the view is still valid. The window might've been closed + // during the switch to the main thread. + if w.view != 0 { + f() + } + }) +} + +func (w *window) Close() { + C.closeWindow(w.window) +} + +// Maximize the window. +func (w *window) Maximize() { + r := C.getScreenFrame(w.window) // the screen size of the window + C.setScreenFrame(w.window, C.CGFloat(0), C.CGFloat(0), r.size.width, r.size.height) +} + +// Center the window. +func (w *window) Center() { + r := C.getScreenFrame(w.window) // the screen size of the window + + screenScale := float32(C.getScreenBackingScale()) + sz := w.config.Size.Div(int(screenScale)) + x := (int(r.size.width) - sz.X) / 2 + y := (int(r.size.height) - sz.Y) / 2 + + C.setScreenFrame(w.window, C.CGFloat(x), C.CGFloat(y), C.CGFloat(sz.X), C.CGFloat(sz.Y)) +} + +func (w *window) setStage(stage system.Stage) { + if stage == w.stage { + return + } + w.stage = stage + w.w.Event(system.StageEvent{Stage: stage}) +} + +//export gio_onKeys +func gio_onKeys(view C.CFTypeRef, cstr *C.char, ti C.double, mods C.NSUInteger, keyDown C.bool) { + str := C.GoString(cstr) + kmods := convertMods(mods) + ks := key.Release + if keyDown { + ks = key.Press + } + w := mustView(view) + for _, k := range str { + if n, ok := convertKey(k); ok { + w.w.Event(key.Event{ + Name: n, + Modifiers: kmods, + State: ks, + }) + } + } +} + +//export gio_onText +func gio_onText(view C.CFTypeRef, cstr *C.char) { + str := C.GoString(cstr) + w := mustView(view) + w.w.Event(key.EditEvent{Text: str}) +} + +//export gio_onMouse +func gio_onMouse(view C.CFTypeRef, cdir C.int, cbtns C.NSUInteger, x, y, dx, dy C.CGFloat, ti C.double, mods C.NSUInteger) { + var typ pointer.Type + switch cdir { + case C.MOUSE_MOVE: + typ = pointer.Move + case C.MOUSE_UP: + typ = pointer.Release + case C.MOUSE_DOWN: + typ = pointer.Press + case C.MOUSE_SCROLL: + typ = pointer.Scroll + default: + panic("invalid direction") + } + var btns pointer.Buttons + if cbtns&(1<<0) != 0 { + btns |= pointer.ButtonPrimary + } + if cbtns&(1<<1) != 0 { + btns |= pointer.ButtonSecondary + } + if cbtns&(1<<2) != 0 { + btns |= pointer.ButtonTertiary + } + t := time.Duration(float64(ti)*float64(time.Second) + .5) + w := mustView(view) + xf, yf := float32(x)*w.scale, float32(y)*w.scale + dxf, dyf := float32(dx)*w.scale, float32(dy)*w.scale + w.w.Event(pointer.Event{ + Type: typ, + Source: pointer.Mouse, + Time: t, + Buttons: btns, + Position: f32.Point{X: xf, Y: yf}, + Scroll: f32.Point{X: dxf, Y: dyf}, + Modifiers: convertMods(mods), + }) +} + +//export gio_onDraw +func gio_onDraw(view C.CFTypeRef) { + w := mustView(view) + w.draw() +} + +//export gio_onFocus +func gio_onFocus(view C.CFTypeRef, focus C.int) { + w := mustView(view) + w.w.Event(key.FocusEvent{Focus: focus == 1}) + w.SetCursor(w.cursor) +} + +//export gio_onChangeScreen +func gio_onChangeScreen(view C.CFTypeRef, did uint64) { + w := mustView(view) + w.displayLink.SetDisplayID(did) +} + +func (w *window) draw() { + w.scale = float32(C.getViewBackingScale(w.view)) + wf, hf := float32(C.viewWidth(w.view)), float32(C.viewHeight(w.view)) + sz := image.Point{ + X: int(wf*w.scale + .5), + Y: int(hf*w.scale + .5), + } + if sz != w.config.Size { + w.config.Size = sz + w.w.Event(ConfigEvent{Config: w.config}) + } + if sz.X == 0 || sz.Y == 0 { + return + } + cfg := configFor(w.scale) + w.setStage(system.StageRunning) + w.w.Event(frameEvent{ + FrameEvent: system.FrameEvent{ + Now: time.Now(), + Size: w.config.Size, + Metric: cfg, + }, + Sync: true, + }) +} + +func configFor(scale float32) unit.Metric { + return unit.Metric{ + PxPerDp: scale, + PxPerSp: scale, + } +} + +//export gio_onClose +func gio_onClose(view C.CFTypeRef) { + w := mustView(view) + w.w.Event(ViewEvent{}) + deleteView(view) + w.w.Event(system.DestroyEvent{}) + w.displayLink.Close() + C.CFRelease(w.view) + C.CFRelease(w.window) + w.view = 0 + w.window = 0 + w.displayLink = nil +} + +//export gio_onHide +func gio_onHide(view C.CFTypeRef) { + w := mustView(view) + w.setStage(system.StagePaused) +} + +//export gio_onShow +func gio_onShow(view C.CFTypeRef) { + w := mustView(view) + w.setStage(system.StageRunning) +} + +//export gio_onFullscreen +func gio_onFullscreen(view C.CFTypeRef) { + w := mustView(view) + w.config.Mode = Fullscreen + w.w.Event(ConfigEvent{Config: w.config}) +} + +//export gio_onWindowed +func gio_onWindowed(view C.CFTypeRef) { + w := mustView(view) + w.config.Mode = Windowed + w.w.Event(ConfigEvent{Config: w.config}) +} + +//export gio_onAppHide +func gio_onAppHide() { + for _, w := range viewMap { + w.setStage(system.StagePaused) + } +} + +//export gio_onAppShow +func gio_onAppShow() { + for _, w := range viewMap { + w.setStage(system.StageRunning) + } +} + +//export gio_onFinishLaunching +func gio_onFinishLaunching() { + close(launched) +} + +func newWindow(win *callbacks, options []Option) error { + <-launched + errch := make(chan error) + runOnMain(func() { + w, err := newOSWindow() + if err != nil { + errch <- err + return + } + errch <- nil + w.w = win + w.window = C.gio_createWindow(w.view, nil, 0, 0, 0, 0, 0, 0) + win.SetDriver(w) + w.Configure(options) + if nextTopLeft.x == 0 && nextTopLeft.y == 0 { + // cascadeTopLeftFromPoint treats (0, 0) as a no-op, + // and just returns the offset we need for the first window. + nextTopLeft = C.cascadeTopLeftFromPoint(w.window, nextTopLeft) + } + nextTopLeft = C.cascadeTopLeftFromPoint(w.window, nextTopLeft) + C.makeKeyAndOrderFront(w.window) + layer := C.layerForView(w.view) + w.w.Event(ViewEvent{View: uintptr(w.view), Layer: uintptr(layer)}) + }) + return <-errch +} + +func newOSWindow() (*window, error) { + view := C.gio_createView() + if view == 0 { + return nil, errors.New("CreateWindow: failed to create view") + } + scale := float32(C.getViewBackingScale(view)) + w := &window{ + view: view, + scale: scale, + } + dl, err := NewDisplayLink(func() { + w.runOnMain(func() { + C.setNeedsDisplay(w.view) + }) + }) + w.displayLink = dl + if err != nil { + C.CFRelease(view) + return nil, err + } + insertView(view, w) + return w, nil +} + +func osMain() { + C.gio_main() +} + +func convertKey(k rune) (string, bool) { + var n string + switch k { + case 0x1b: + n = key.NameEscape + case C.NSLeftArrowFunctionKey: + n = key.NameLeftArrow + case C.NSRightArrowFunctionKey: + n = key.NameRightArrow + case C.NSUpArrowFunctionKey: + n = key.NameUpArrow + case C.NSDownArrowFunctionKey: + n = key.NameDownArrow + case 0xd: + n = key.NameReturn + case 0x3: + n = key.NameEnter + case C.NSHomeFunctionKey: + n = key.NameHome + case C.NSEndFunctionKey: + n = key.NameEnd + case 0x7f: + n = key.NameDeleteBackward + case C.NSDeleteFunctionKey: + n = key.NameDeleteForward + case C.NSPageUpFunctionKey: + n = key.NamePageUp + case C.NSPageDownFunctionKey: + n = key.NamePageDown + case C.NSF1FunctionKey: + n = "F1" + case C.NSF2FunctionKey: + n = "F2" + case C.NSF3FunctionKey: + n = "F3" + case C.NSF4FunctionKey: + n = "F4" + case C.NSF5FunctionKey: + n = "F5" + case C.NSF6FunctionKey: + n = "F6" + case C.NSF7FunctionKey: + n = "F7" + case C.NSF8FunctionKey: + n = "F8" + case C.NSF9FunctionKey: + n = "F9" + case C.NSF10FunctionKey: + n = "F10" + case C.NSF11FunctionKey: + n = "F11" + case C.NSF12FunctionKey: + n = "F12" + case 0x09, 0x19: + n = key.NameTab + case 0x20: + n = key.NameSpace + default: + k = unicode.ToUpper(k) + if !unicode.IsPrint(k) { + return "", false + } + n = string(k) + } + return n, true +} + +func convertMods(mods C.NSUInteger) key.Modifiers { + var kmods key.Modifiers + if mods&C.NSAlternateKeyMask != 0 { + kmods |= key.ModAlt + } + if mods&C.NSControlKeyMask != 0 { + kmods |= key.ModCtrl + } + if mods&C.NSCommandKeyMask != 0 { + kmods |= key.ModCommand + } + if mods&C.NSShiftKeyMask != 0 { + kmods |= key.ModShift + } + return kmods +} + +func (_ ViewEvent) ImplementsEvent() {} diff --git a/vendor/gioui.org/app/internal/window/os_macos.m b/vendor/gioui.org/app/os_macos.m index b8c0dee..f65099b 100644 --- a/vendor/gioui.org/app/internal/window/os_macos.m +++ b/vendor/gioui.org/app/os_macos.m @@ -6,6 +6,8 @@ #include "_cgo_export.h" +__attribute__ ((visibility ("hidden"))) CALayer *gio_layerFactory(void); + @interface GioAppDelegate : NSObject<NSApplicationDelegate> @end @@ -21,6 +23,14 @@ NSWindow *window = (NSWindow *)[notification object]; gio_onShow((__bridge CFTypeRef)window.contentView); } +- (void)windowWillEnterFullScreen:(NSNotification *)notification { + NSWindow *window = (NSWindow *)[notification object]; + gio_onFullscreen((__bridge CFTypeRef)window.contentView); +} +- (void)windowWillExitFullScreen:(NSNotification *)notification { + NSWindow *window = (NSWindow *)[notification object]; + gio_onWindowed((__bridge CFTypeRef)window.contentView); +} - (void)windowDidChangeScreen:(NSNotification *)notification { NSWindow *window = (NSWindow *)[notification object]; CGDirectDisplayID dispID = [[[window screen] deviceDescription][@"NSScreenNumber"] unsignedIntValue]; @@ -29,11 +39,11 @@ } - (void)windowDidBecomeKey:(NSNotification *)notification { NSWindow *window = (NSWindow *)[notification object]; - gio_onFocus((__bridge CFTypeRef)window.contentView, YES); + gio_onFocus((__bridge CFTypeRef)window.contentView, 1); } - (void)windowDidResignKey:(NSNotification *)notification { NSWindow *window = (NSWindow *)[notification object]; - gio_onFocus((__bridge CFTypeRef)window.contentView, NO); + gio_onFocus((__bridge CFTypeRef)window.contentView, 0); } - (void)windowWillClose:(NSNotification *)notification { NSWindow *window = (NSWindow *)[notification object]; @@ -42,49 +52,92 @@ } @end -// Delegates are weakly referenced from their peers. Nothing -// else holds a strong reference to our window delegate, so -// keep a single global reference instead. -static GioWindowDelegate *globalWindowDel; - -void gio_writeClipboard(unichar *chars, NSUInteger length) { - @autoreleasepool { - NSString *s = [NSString string]; - if (length > 0) { - s = [NSString stringWithCharacters:chars length:length]; - } - NSPasteboard *p = NSPasteboard.generalPasteboard; - [p declareTypes:@[NSPasteboardTypeString] owner:nil]; - [p setString:s forType:NSPasteboardTypeString]; +static void handleMouse(NSView *view, NSEvent *event, int typ, CGFloat dx, CGFloat dy) { + NSPoint p = [view convertPoint:[event locationInWindow] fromView:nil]; + if (!event.hasPreciseScrollingDeltas) { + // dx and dy are in rows and columns. + dx *= 10; + dy *= 10; } + // Origin is in the lower left corner. Convert to upper left. + CGFloat height = view.bounds.size.height; + gio_onMouse((__bridge CFTypeRef)view, typ, [NSEvent pressedMouseButtons], p.x, height - p.y, dx, dy, [event timestamp], [event modifierFlags]); } -CFTypeRef gio_readClipboard(void) { - @autoreleasepool { - NSPasteboard *p = NSPasteboard.generalPasteboard; - NSString *content = [p stringForType:NSPasteboardTypeString]; - return (__bridge_retained CFTypeRef)content; - } -} +@interface GioView : NSView <CALayerDelegate> +@end -CGFloat gio_viewHeight(CFTypeRef viewRef) { - NSView *view = (__bridge NSView *)viewRef; - return [view bounds].size.height; +@implementation GioView +- (void)setFrameSize:(NSSize)newSize { + [super setFrameSize:newSize]; + [self setNeedsDisplay:YES]; } - -CGFloat gio_viewWidth(CFTypeRef viewRef) { - NSView *view = (__bridge NSView *)viewRef; - return [view bounds].size.width; +// drawRect is called when OpenGL is used, displayLayer otherwise. +// Don't know why. +- (void)drawRect:(NSRect)r { + gio_onDraw((__bridge CFTypeRef)self); } - -CGFloat gio_getScreenBackingScale(void) { - return [NSScreen.mainScreen backingScaleFactor]; +- (void)displayLayer:(CALayer *)layer { + layer.contentsScale = self.window.backingScaleFactor; + gio_onDraw((__bridge CFTypeRef)self); } - -CGFloat gio_getViewBackingScale(CFTypeRef viewRef) { - NSView *view = (__bridge NSView *)viewRef; - return [view.window backingScaleFactor]; +- (CALayer *)makeBackingLayer { + CALayer *layer = gio_layerFactory(); + layer.delegate = self; + return layer; +} +- (void)mouseDown:(NSEvent *)event { + handleMouse(self, event, MOUSE_DOWN, 0, 0); +} +- (void)mouseUp:(NSEvent *)event { + handleMouse(self, event, MOUSE_UP, 0, 0); +} +- (void)middleMouseDown:(NSEvent *)event { + handleMouse(self, event, MOUSE_DOWN, 0, 0); +} +- (void)middletMouseUp:(NSEvent *)event { + handleMouse(self, event, MOUSE_UP, 0, 0); +} +- (void)rightMouseDown:(NSEvent *)event { + handleMouse(self, event, MOUSE_DOWN, 0, 0); +} +- (void)rightMouseUp:(NSEvent *)event { + handleMouse(self, event, MOUSE_UP, 0, 0); } +- (void)mouseMoved:(NSEvent *)event { + handleMouse(self, event, MOUSE_MOVE, 0, 0); +} +- (void)mouseDragged:(NSEvent *)event { + handleMouse(self, event, MOUSE_MOVE, 0, 0); +} +- (void)scrollWheel:(NSEvent *)event { + CGFloat dx = -event.scrollingDeltaX; + CGFloat dy = -event.scrollingDeltaY; + handleMouse(self, event, MOUSE_SCROLL, dx, dy); +} +- (void)keyDown:(NSEvent *)event { + NSString *keys = [event charactersIgnoringModifiers]; + gio_onKeys((__bridge CFTypeRef)self, (char *)[keys UTF8String], [event timestamp], [event modifierFlags], true); + [self interpretKeyEvents:[NSArray arrayWithObject:event]]; +} +- (void)keyUp:(NSEvent *)event { + NSString *keys = [event charactersIgnoringModifiers]; + gio_onKeys((__bridge CFTypeRef)self, (char *)[keys UTF8String], [event timestamp], [event modifierFlags], false); +} +- (void)insertText:(id)string { + const char *utf8 = [string UTF8String]; + gio_onText((__bridge CFTypeRef)self, (char *)utf8); +} +- (void)doCommandBySelector:(SEL)sel { + // Don't pass commands up the responder chain. + // They will end up in a beep. +} +@end + +// Delegates are weakly referenced from their peers. Nothing +// else holds a strong reference to our window delegate, so +// keep a single global reference instead. +static GioWindowDelegate *globalWindowDel; static CVReturn displayLinkCallback(CVDisplayLinkRef dl, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext) { gio_onFrameCallback(dl); @@ -114,14 +167,47 @@ void gio_setDisplayLinkDisplay(CFTypeRef dl, uint64_t did) { CVDisplayLinkSetCurrentCGDisplay((CVDisplayLinkRef)dl, (CGDirectDisplayID)did); } -NSPoint gio_cascadeTopLeftFromPoint(CFTypeRef windowRef, NSPoint topLeft) { - NSWindow *window = (__bridge NSWindow *)windowRef; - return [window cascadeTopLeftFromPoint:topLeft]; +void gio_hideCursor() { + @autoreleasepool { + [NSCursor hide]; + } +} + +void gio_showCursor() { + @autoreleasepool { + [NSCursor unhide]; + } } -void gio_makeKeyAndOrderFront(CFTypeRef windowRef) { - NSWindow *window = (__bridge NSWindow *)windowRef; - [window makeKeyAndOrderFront:nil]; +void gio_setCursor(NSUInteger curID) { + @autoreleasepool { + switch (curID) { + case 1: + [NSCursor.arrowCursor set]; + break; + case 2: + [NSCursor.IBeamCursor set]; + break; + case 3: + [NSCursor.pointingHandCursor set]; + break; + case 4: + [NSCursor.crosshairCursor set]; + break; + case 5: + [NSCursor.resizeLeftRightCursor set]; + break; + case 6: + [NSCursor.resizeUpDownCursor set]; + break; + case 7: + [NSCursor.openHandCursor set]; + break; + default: + [NSCursor.arrowCursor set]; + break; + } + } } CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight) { @@ -143,7 +229,9 @@ CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, window.contentMaxSize = NSMakeSize(maxWidth, maxHeight); } [window setAcceptsMouseMovedEvents:YES]; - window.title = [NSString stringWithUTF8String: title]; + if (title != nil) { + window.title = [NSString stringWithUTF8String: title]; + } NSView *view = (__bridge NSView *)viewRef; [window setContentView:view]; [window makeFirstResponder:view]; @@ -153,14 +241,20 @@ CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, } } -void gio_close(CFTypeRef windowRef) { - NSWindow* window = (__bridge NSWindow *)windowRef; - [window performClose:nil]; +CFTypeRef gio_createView(void) { + @autoreleasepool { + NSRect frame = NSMakeRect(0, 0, 0, 0); + GioView* view = [[GioView alloc] initWithFrame:frame]; + view.wantsLayer = YES; + view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize; + return CFBridgingRetain(view); + } } @implementation GioAppDelegate - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { - [[NSRunningApplication currentApplication] activateWithOptions:(NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps)]; + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + [NSApp activateIgnoringOtherApps:YES]; gio_onFinishLaunching(); } - (void)applicationDidHide:(NSNotification *)aNotification { @@ -176,7 +270,6 @@ void gio_main() { [NSApplication sharedApplication]; GioAppDelegate *del = [[GioAppDelegate alloc] init]; [NSApp setDelegate:del]; - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; NSMenuItem *mainMenu = [NSMenuItem new]; diff --git a/vendor/gioui.org/app/os_unix.go b/vendor/gioui.org/app/os_unix.go new file mode 100644 index 0000000..ee831b3 --- /dev/null +++ b/vendor/gioui.org/app/os_unix.go @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build (linux && !android) || freebsd || openbsd +// +build linux,!android freebsd openbsd + +package app + +import ( + "errors" + "unsafe" +) + +type ViewEvent struct { + // Display is a pointer to the X11 Display created by XOpenDisplay. + Display unsafe.Pointer + // Window is the X11 window ID as returned by XCreateWindow. + Window uintptr +} + +func osMain() { + select {} +} + +type windowDriver func(*callbacks, []Option) error + +// Instead of creating files with build tags for each combination of wayland +/- x11 +// let each driver initialize these variables with their own version of createWindow. +var wlDriver, x11Driver windowDriver + +func newWindow(window *callbacks, options []Option) error { + var errFirst error + for _, d := range []windowDriver{x11Driver, wlDriver} { + if d == nil { + continue + } + err := d(window, options) + if err == nil { + return nil + } + if errFirst == nil { + errFirst = err + } + } + if errFirst != nil { + return errFirst + } + return errors.New("app: no window driver available") +} + +func (_ ViewEvent) ImplementsEvent() {} diff --git a/vendor/gioui.org/app/internal/window/os_wayland.c b/vendor/gioui.org/app/os_wayland.c index 5c1c075..b137e1f 100644 --- a/vendor/gioui.org/app/internal/window/os_wayland.c +++ b/vendor/gioui.org/app/os_wayland.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: Unlicense OR MIT -// +build linux,!android,!nowayland freebsd +//go:build ((linux && !android) || freebsd) && !nowayland +// +build linux,!android freebsd +// +build !nowayland #include <wayland-client.h> #include "wayland_xdg_shell.h" diff --git a/vendor/gioui.org/app/internal/window/os_wayland.go b/vendor/gioui.org/app/os_wayland.go index 4a456ce..ac7aa17 100644 --- a/vendor/gioui.org/app/internal/window/os_wayland.go +++ b/vendor/gioui.org/app/os_wayland.go @@ -1,8 +1,10 @@ // SPDX-License-Identifier: Unlicense OR MIT -// +build linux,!android,!nowayland freebsd +//go:build ((linux && !android) || freebsd) && !nowayland +// +build linux,!android freebsd +// +build !nowayland -package window +package app import ( "bytes" @@ -19,14 +21,16 @@ import ( "time" "unsafe" + syscall "golang.org/x/sys/unix" + "gioui.org/app/internal/xkb" "gioui.org/f32" "gioui.org/internal/fling" + "gioui.org/io/clipboard" "gioui.org/io/key" "gioui.org/io/pointer" "gioui.org/io/system" "gioui.org/unit" - syscall "golang.org/x/sys/unix" ) // Use wayland-scanner to generate glue code for the xdg-shell and xdg-decoration extensions. @@ -39,9 +43,9 @@ import ( //go:generate wayland-scanner client-header /usr/share/wayland-protocols/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml wayland_xdg_decoration.h //go:generate wayland-scanner private-code /usr/share/wayland-protocols/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml wayland_xdg_decoration.c -//go:generate sed -i "1s;^;// +build linux,!android,!nowayland freebsd\\n\\n;" wayland_xdg_shell.c -//go:generate sed -i "1s;^;// +build linux,!android,!nowayland freebsd\\n\\n;" wayland_xdg_decoration.c -//go:generate sed -i "1s;^;// +build linux,!android,!nowayland freebsd\\n\\n;" wayland_text_input.c +//go:generate sed -i "1s;^;//go:build ((linux \\&\\& !android) || freebsd) \\&\\& !nowayland\\n// +build linux,!android freebsd\\n// +build !nowayland\\n\\n;" wayland_xdg_shell.c +//go:generate sed -i "1s;^;//go:build ((linux \\&\\& !android) || freebsd) \\&\\& !nowayland\\n// +build linux,!android freebsd\\n// +build !nowayland\\n\\n;" wayland_xdg_decoration.c +//go:generate sed -i "1s;^;//go:build ((linux \\&\\& !android) || freebsd) \\&\\& !nowayland\\n// +build linux,!android freebsd\\n// +build !nowayland\\n\\n;" wayland_text_input.c /* #cgo linux pkg-config: wayland-client wayland-cursor @@ -133,7 +137,7 @@ type repeatState struct { delay time.Duration key uint32 - win Callbacks + win *callbacks stopC chan struct{} start time.Duration @@ -143,7 +147,7 @@ type repeatState struct { } type window struct { - w Callbacks + w *callbacks disp *wlDisplay surf *C.struct_wl_surface wmSurf *C.struct_xdg_surface @@ -177,19 +181,17 @@ type window struct { dead bool lastFrameCallback *C.struct_wl_callback - mu sync.Mutex animating bool needAck bool // The most recent configure serial waiting to be ack'ed. serial C.uint32_t - width int - height int newScale bool scale int - // readClipboard tracks whether a ClipboardEvent is requested. - readClipboard bool - // writeClipboard is set whenever a clipboard write is requested. - writeClipboard *string + // size is the unscaled window size (unlike config.Size which is scaled). + size image.Point + config Config + + wakeups chan struct{} } type poller struct { @@ -218,25 +220,34 @@ var callbackMap sync.Map // order of preference. var clipboardMimeTypes = []string{"text/plain;charset=utf8", "UTF8_STRING", "text/plain", "TEXT", "STRING"} +var ( + newWaylandEGLContext func(w *window) (context, error) + newWaylandVulkanContext func(w *window) (context, error) +) + func init() { wlDriver = newWLWindow } -func newWLWindow(window Callbacks, opts *Options) error { +func newWLWindow(callbacks *callbacks, options []Option) error { d, err := newWLDisplay() if err != nil { return err } - w, err := d.createNativeWindow(opts) + w, err := d.createNativeWindow(options) if err != nil { d.destroy() return err } - w.w = window + w.w = callbacks go func() { defer d.destroy() defer w.destroy() + w.w.SetDriver(w) + // Finish and commit setup from createNativeWindow. + w.Configure(options) + C.wl_surface_commit(w.surf) if err := w.loop(); err != nil { panic(err) } @@ -280,13 +291,16 @@ func (d *wlDisplay) readClipboard() (io.ReadCloser, error) { if err != nil { return nil, err } + // wl_data_offer_receive performs and implicit dup(2) of the write end + // of the pipe. Close our version. + defer w.Close() cmimeType := C.CString(s.mimeType) defer C.free(unsafe.Pointer(cmimeType)) C.wl_data_offer_receive(s.clipboard, cmimeType, C.int(w.Fd())) return r, nil } -func (d *wlDisplay) createNativeWindow(opts *Options) (*window, error) { +func (d *wlDisplay) createNativeWindow(options []Option) (*window, error) { if d.compositor == nil { return nil, errors.New("wayland: no compositor available") } @@ -313,6 +327,7 @@ func (d *wlDisplay) createNativeWindow(opts *Options) (*window, error) { newScale: scale != 1, ppdp: ppdp, ppsp: ppdp, + wakeups: make(chan struct{}, 1), } w.surf = C.wl_compositor_create_surface(d.compositor) if w.surf == nil { @@ -351,20 +366,13 @@ func (d *wlDisplay) createNativeWindow(opts *Options) (*window, error) { C.wl_surface_add_listener(w.surf, &C.gio_surface_listener, unsafe.Pointer(w.surf)) C.xdg_surface_add_listener(w.wmSurf, &C.gio_xdg_surface_listener, unsafe.Pointer(w.surf)) C.xdg_toplevel_add_listener(w.topLvl, &C.gio_xdg_toplevel_listener, unsafe.Pointer(w.surf)) - title := C.CString(opts.Title) - C.xdg_toplevel_set_title(w.topLvl, title) - C.free(unsafe.Pointer(title)) - _, _, cfg := w.config() - w.width = cfg.Px(opts.Width) - w.height = cfg.Px(opts.Height) if d.decor != nil { // Request server side decorations. w.decor = C.zxdg_decoration_manager_v1_get_toplevel_decoration(d.decor, w.topLvl) C.zxdg_toplevel_decoration_v1_set_mode(w.decor, C.ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE) } w.updateOpaqueRegion() - C.wl_surface_commit(w.surf) return w, nil } @@ -471,10 +479,8 @@ func gio_onSeatName(data unsafe.Pointer, seat *C.struct_wl_seat, name *C.char) { //export gio_onXdgSurfaceConfigure func gio_onXdgSurfaceConfigure(data unsafe.Pointer, wmSurf *C.struct_xdg_surface, serial C.uint32_t) { w := callbackLoad(data).(*window) - w.mu.Lock() w.serial = serial w.needAck = true - w.mu.Unlock() w.setStage(system.StageRunning) w.draw(true) } @@ -489,10 +495,7 @@ func gio_onToplevelClose(data unsafe.Pointer, topLvl *C.struct_xdg_toplevel) { func gio_onToplevelConfigure(data unsafe.Pointer, topLvl *C.struct_xdg_toplevel, width, height C.int32_t, states *C.struct_wl_array) { w := callbackLoad(data).(*window) if width != 0 && height != 0 { - w.mu.Lock() - defer w.mu.Unlock() - w.width = int(width) - w.height = int(height) + w.size = image.Pt(int(width), int(height)) w.updateOpaqueRegion() } } @@ -584,10 +587,11 @@ func gio_onRegistryGlobal(data unsafe.Pointer, reg *C.struct_wl_registry, name C break } d.seat = &wlSeat{ - disp: d, - name: name, - seat: s, - offers: make(map[*C.struct_wl_data_offer][]string), + disp: d, + name: name, + seat: s, + offers: make(map[*C.struct_wl_data_offer][]string), + touchFoci: make(map[C.int32_t]*window), } callbackStore(unsafe.Pointer(s), d.seat) C.wl_seat_add_listener(s, &C.gio_seat_listener, unsafe.Pointer(s)) @@ -763,16 +767,7 @@ func gio_onPointerEnter(data unsafe.Pointer, pointer *C.struct_wl_pointer, seria s.serial = serial w := callbackLoad(unsafe.Pointer(surf)).(*window) s.pointerFocus = w - // Get images[0]. - img := *w.cursor.cursor.images - buf := C.wl_cursor_image_get_buffer(img) - if buf == nil { - return - } - C.wl_pointer_set_cursor(pointer, serial, w.cursor.surf, C.int32_t(img.hotspot_x), C.int32_t(img.hotspot_y)) - C.wl_surface_attach(w.cursor.surf, buf, 0, 0) - C.wl_surface_damage(w.cursor.surf, 0, 0, C.int32_t(img.width), C.int32_t(img.height)) - C.wl_surface_commit(w.cursor.surf) + w.setCursor(pointer, serial) w.lastPos = f32.Point{X: fromFixed(x), Y: fromFixed(y)} } @@ -804,11 +799,11 @@ func gio_onPointerButton(data unsafe.Pointer, p *C.struct_wl_pointer, serial, t, var btn pointer.Buttons switch wbtn { case BTN_LEFT: - btn = pointer.ButtonLeft + btn = pointer.ButtonPrimary case BTN_RIGHT: - btn = pointer.ButtonRight + btn = pointer.ButtonSecondary case BTN_MIDDLE: - btn = pointer.ButtonMiddle + btn = pointer.ButtonTertiary default: return } @@ -867,15 +862,13 @@ func (w *window) flushFling() { w.fling.xExtrapolation = fling.Extrapolation{} w.fling.yExtrapolation = fling.Extrapolation{} vel := float32(math.Sqrt(float64(estx.Velocity*estx.Velocity + esty.Velocity*esty.Velocity))) - _, _, c := w.config() + _, c := w.getConfig() if !w.fling.anim.Start(c, time.Now(), vel) { return } invDist := 1 / vel w.fling.dir.X = estx.Velocity * invDist w.fling.dir.Y = esty.Velocity * invDist - // Wake up the window loop. - w.disp.wakeup() } //export gio_onPointerAxisSource @@ -903,17 +896,94 @@ func gio_onPointerAxisDiscrete(data unsafe.Pointer, p *C.struct_wl_pointer, axis } func (w *window) ReadClipboard() { - w.mu.Lock() - w.readClipboard = true - w.mu.Unlock() - w.disp.wakeup() + r, err := w.disp.readClipboard() + // Send empty responses on unavailable clipboards or errors. + if r == nil || err != nil { + w.w.Event(clipboard.Event{}) + return + } + // Don't let slow clipboard transfers block event loop. + go func() { + defer r.Close() + data, _ := ioutil.ReadAll(r) + w.w.Event(clipboard.Event{Text: string(data)}) + }() } func (w *window) WriteClipboard(s string) { - w.mu.Lock() - w.writeClipboard = &s - w.mu.Unlock() - w.disp.wakeup() + w.disp.writeClipboard([]byte(s)) +} + +func (w *window) Configure(options []Option) { + _, cfg := w.getConfig() + prev := w.config + cnf := w.config + cnf.apply(cfg, options) + if prev.Size != cnf.Size { + w.size = image.Pt(cnf.Size.X/w.scale, cnf.Size.Y/w.scale) + w.config.Size = cnf.Size + } + if prev.Title != cnf.Title { + w.config.Title = cnf.Title + title := C.CString(cnf.Title) + C.xdg_toplevel_set_title(w.topLvl, title) + C.free(unsafe.Pointer(title)) + } + if w.config != prev { + w.w.Event(ConfigEvent{Config: w.config}) + } +} + +func (w *window) Raise() {} + +func (w *window) SetCursor(name pointer.CursorName) { + ptr := w.disp.seat.pointer + if ptr == nil { + return + } + if name == pointer.CursorNone { + C.wl_pointer_set_cursor(ptr, w.serial, nil, 0, 0) + return + } + switch name { + default: + fallthrough + case pointer.CursorDefault: + name = "left_ptr" + case pointer.CursorText: + name = "xterm" + case pointer.CursorPointer: + name = "hand1" + case pointer.CursorCrossHair: + name = "crosshair" + case pointer.CursorRowResize: + name = "top_side" + case pointer.CursorColResize: + name = "left_side" + case pointer.CursorGrab: + name = "hand1" + } + cname := C.CString(string(name)) + defer C.free(unsafe.Pointer(cname)) + c := C.wl_cursor_theme_get_cursor(w.cursor.theme, cname) + if c == nil { + return + } + w.cursor.cursor = c + w.setCursor(ptr, w.serial) +} + +func (w *window) setCursor(pointer *C.struct_wl_pointer, serial C.uint32_t) { + // Get images[0]. + img := *w.cursor.cursor.images + buf := C.wl_cursor_image_get_buffer(img) + if buf == nil { + return + } + C.wl_pointer_set_cursor(pointer, serial, w.cursor.surf, C.int32_t(img.hotspot_x), C.int32_t(img.hotspot_y)) + C.wl_surface_attach(w.cursor.surf, buf, 0, 0) + C.wl_surface_damage(w.cursor.surf, 0, 0, C.int32_t(img.width), C.int32_t(img.height)) + C.wl_surface_commit(w.cursor.surf) } func (w *window) resetFling() { @@ -963,13 +1033,14 @@ func gio_onKeyboardKey(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, seri t := time.Duration(timestamp) * time.Millisecond s.disp.repeat.Stop(t) w.resetFling() - if state != C.WL_KEYBOARD_KEY_STATE_PRESSED { - return - } kc := mapXKBKeycode(uint32(keyCode)) - for _, e := range w.disp.xkb.DispatchKey(kc) { + ks := mapXKBKeyState(uint32(state)) + for _, e := range w.disp.xkb.DispatchKey(kc, ks) { w.w.Event(e) } + if state != C.WL_KEYBOARD_KEY_STATE_PRESSED { + return + } if w.disp.xkb.IsRepeatKey(kc) { w.disp.repeat.Start(w, kc, t) } @@ -980,6 +1051,15 @@ func mapXKBKeycode(keyCode uint32) uint32 { return keyCode + 8 } +func mapXKBKeyState(state uint32) key.State { + switch state { + case C.WL_KEYBOARD_KEY_STATE_RELEASED: + return key.Release + default: + return key.Press + } +} + func (r *repeatState) Start(w *window, keyCode uint32, t time.Duration) { if r.rate <= 0 { return @@ -1045,7 +1125,7 @@ func (r *repeatState) Repeat(d *wlDisplay) { if r.last+delay > now { break } - for _, e := range d.xkb.DispatchKey(r.key) { + for _, e := range d.xkb.DispatchKey(r.key, key.Press) { r.win.Event(e) } r.last += delay @@ -1068,43 +1148,21 @@ func (w *window) loop() error { if err := w.disp.dispatch(&p); err != nil { return err } + select { + case <-w.wakeups: + w.w.Event(wakeupEvent{}) + default: + } if w.dead { w.w.Event(system.DestroyEvent{}) break } - w.process() + // pass false to skip unnecessary drawing. + w.draw(false) } return nil } -func (w *window) process() { - w.mu.Lock() - readClipboard := w.readClipboard - writeClipboard := w.writeClipboard - w.readClipboard = false - w.writeClipboard = nil - w.mu.Unlock() - if readClipboard { - r, err := w.disp.readClipboard() - // Send empty responses on unavailable clipboards or errors. - if r == nil || err != nil { - w.w.Event(system.ClipboardEvent{}) - return - } - // Don't let slow clipboard transfers block event loop. - go func() { - defer r.Close() - data, _ := ioutil.ReadAll(r) - w.w.Event(system.ClipboardEvent{Text: string(data)}) - }() - } - if writeClipboard != nil { - w.disp.writeClipboard([]byte(*writeClipboard)) - } - // pass false to skip unnecessary drawing. - w.draw(false) -} - func (d *wlDisplay) dispatch(p *poller) error { dispfd := C.wl_display_get_fd(d.disp) // Poll for events and notifications. @@ -1147,11 +1205,16 @@ func (d *wlDisplay) dispatch(p *poller) error { return nil } +func (w *window) Wakeup() { + select { + case w.wakeups <- struct{}{}: + default: + } + w.disp.wakeup() +} + func (w *window) SetAnimating(anim bool) { - w.mu.Lock() w.animating = anim - w.mu.Unlock() - w.disp.wakeup() } // Wakeup wakes up the event loop through the notification pipe. @@ -1322,7 +1385,7 @@ func (w *window) onPointerMotion(x, y C.wl_fixed_t, t C.uint32_t) { func (w *window) updateOpaqueRegion() { reg := C.wl_compositor_create_region(w.disp.compositor) - C.wl_region_add(reg, 0, 0, C.int32_t(w.width), C.int32_t(w.height)) + C.wl_region_add(reg, 0, 0, C.int32_t(w.size.X), C.int32_t(w.size.Y)) C.wl_surface_set_opaque_region(w.surf, reg) C.wl_region_destroy(reg) } @@ -1340,12 +1403,10 @@ func (w *window) updateOutputs() { } } } - w.mu.Lock() if found && scale != w.scale { w.scale = scale w.newScale = true } - w.mu.Unlock() if !found { w.setStage(system.StagePaused) } else { @@ -1354,9 +1415,9 @@ func (w *window) updateOutputs() { } } -func (w *window) config() (int, int, unit.Metric) { - width, height := w.width*w.scale, w.height*w.scale - return width, height, unit.Metric{ +func (w *window) getConfig() (image.Point, unit.Metric) { + size := w.size.Mul(w.scale) + return size, unit.Metric{ PxPerDp: w.ppdp * float32(w.scale), PxPerSp: w.ppsp * float32(w.scale), } @@ -1364,14 +1425,16 @@ func (w *window) config() (int, int, unit.Metric) { func (w *window) draw(sync bool) { w.flushScroll() - w.mu.Lock() anim := w.animating || w.fling.anim.Active() dead := w.dead - w.mu.Unlock() if dead || (!anim && !sync) { return } - width, height, cfg := w.config() + size, cfg := w.getConfig() + if size != w.config.Size { + w.config.Size = size + w.w.Event(ConfigEvent{Config: w.config}) + } if cfg == (unit.Metric{}) { return } @@ -1380,13 +1443,10 @@ func (w *window) draw(sync bool) { // Use the surface as listener data for gio_onFrameDone. C.wl_callback_add_listener(w.lastFrameCallback, &C.gio_callback_listener, unsafe.Pointer(w.surf)) } - w.w.Event(FrameEvent{ + w.w.Event(frameEvent{ FrameEvent: system.FrameEvent{ - Now: time.Now(), - Size: image.Point{ - X: width, - Y: height, - }, + Now: time.Now(), + Size: w.config.Size, Metric: cfg, }, Sync: sync, @@ -1398,7 +1458,7 @@ func (w *window) setStage(s system.Stage) { return } w.stage = s - w.w.Event(system.StageEvent{s}) + w.w.Event(system.StageEvent{Stage: s}) } func (w *window) display() *C.struct_wl_display { @@ -1410,18 +1470,50 @@ func (w *window) surface() (*C.struct_wl_surface, int, int) { C.xdg_surface_ack_configure(w.wmSurf, w.serial) w.needAck = false } - width, height, scale := w.width, w.height, w.scale if w.newScale { - C.wl_surface_set_buffer_scale(w.surf, C.int32_t(scale)) + C.wl_surface_set_buffer_scale(w.surf, C.int32_t(w.scale)) w.newScale = false } - return w.surf, width * scale, height * scale + sz, _ := w.getConfig() + return w.surf, sz.X, sz.Y } func (w *window) ShowTextInput(show bool) {} -// Close the window. Not implemented for Wayland. -func (w *window) Close() {} +func (w *window) SetInputHint(_ key.InputHint) {} + +// Close the window. +func (w *window) Close() { + w.dead = true +} + +// Maximize the window. Not implemented for Wayland. +func (w *window) Maximize() {} + +// Center the window. Not implemented for Wayland. +func (w *window) Center() {} + +func (w *window) NewContext() (context, error) { + var firstErr error + if f := newWaylandVulkanContext; f != nil { + c, err := f(w) + if err == nil { + return c, nil + } + firstErr = err + } + if f := newWaylandEGLContext; f != nil { + c, err := f(w) + if err == nil { + return c, nil + } + firstErr = err + } + if firstErr != nil { + return nil, firstErr + } + return nil, errors.New("wayland: no available GPU backends") +} // detectUIScale reports the system UI scale, or 1.0 if it fails. func detectUIScale() float32 { diff --git a/vendor/gioui.org/app/internal/window/os_windows.go b/vendor/gioui.org/app/os_windows.go index 247f10a..ecbffb4 100644 --- a/vendor/gioui.org/app/internal/window/os_windows.go +++ b/vendor/gioui.org/app/os_windows.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Unlicense OR MIT -package window +package app import ( "errors" @@ -13,23 +13,23 @@ import ( "sync" "time" "unicode" - "unicode/utf16" "unsafe" syscall "golang.org/x/sys/windows" "gioui.org/app/internal/windows" "gioui.org/unit" + gowindows "golang.org/x/sys/windows" "gioui.org/f32" + "gioui.org/io/clipboard" "gioui.org/io/key" "gioui.org/io/pointer" "gioui.org/io/system" ) -type winConstraints struct { - minWidth, minHeight int32 - maxWidth, maxHeight int32 +type ViewEvent struct { + HWND uintptr } type winDeltas struct { @@ -40,69 +40,80 @@ type winDeltas struct { type window struct { hwnd syscall.Handle hdc syscall.Handle - w Callbacks - width int - height int + w *callbacks stage system.Stage - dead bool pointerBtns pointer.Buttons - mu sync.Mutex + // cursorIn tracks whether the cursor was inside the window according + // to the most recent WM_SETCURSOR. + cursorIn bool + cursor syscall.Handle + + // placement saves the previous window position when in full screen mode. + placement *windows.WindowPlacement + animating bool - minmax winConstraints deltas winDeltas - opts *Options + config Config } -const _WM_REDRAW = windows.WM_USER + 0 +const _WM_WAKEUP = windows.WM_USER + iota type gpuAPI struct { priority int - initializer func(w *window) (Context, error) + initializer func(w *window) (context, error) } -// backends is the list of potential Context -// implementations. -var backends []gpuAPI +// drivers is the list of potential Context implementations. +var drivers []gpuAPI // winMap maps win32 HWNDs to *windows. var winMap sync.Map +// iconID is the ID of the icon in the resource file. +const iconID = 1 + var resources struct { once sync.Once // handle is the module handle from GetModuleHandle. handle syscall.Handle // class is the Gio window class from RegisterClassEx. class uint16 - // cursor is the arrow cursor resource + // cursor is the arrow cursor resource. cursor syscall.Handle } -func Main() { +func osMain() { select {} } -func NewWindow(window Callbacks, opts *Options) error { +func newWindow(window *callbacks, options []Option) error { cerr := make(chan error) go func() { - // Call win32 API from a single OS thread. + // GetMessage and PeekMessage can filter on a window HWND, but + // then thread-specific messages such as WM_QUIT are ignored. + // Instead lock the thread so window messages arrive through + // unfiltered GetMessage calls. runtime.LockOSThread() - w, err := createNativeWindow(opts) + w, err := createNativeWindow() if err != nil { cerr <- err return } - defer w.destroy() cerr <- nil winMap.Store(w.hwnd, w) defer winMap.Delete(w.hwnd) w.w = window w.w.SetDriver(w) - defer w.w.Event(system.DestroyEvent{}) + w.w.Event(ViewEvent{HWND: uintptr(w.hwnd)}) + w.Configure(options) windows.ShowWindow(w.hwnd, windows.SW_SHOWDEFAULT) windows.SetForegroundWindow(w.hwnd) windows.SetFocus(w.hwnd) + // Since the window class for the cursor is null, + // set it here to show the cursor. + w.SetCursor(pointer.CursorDefault) if err := w.loop(); err != nil { panic(err) } @@ -118,17 +129,18 @@ func initResources() error { return err } resources.handle = hInst - curs, err := windows.LoadCursor(windows.IDC_ARROW) + c, err := windows.LoadCursor(windows.IDC_ARROW) if err != nil { return err } - resources.cursor = curs + resources.cursor = c + icon, _ := windows.LoadImage(hInst, iconID, windows.IMAGE_ICON, 0, 0, windows.LR_DEFAULTSIZE|windows.LR_SHARED) wcls := windows.WndClassEx{ CbSize: uint32(unsafe.Sizeof(windows.WndClassEx{})), Style: windows.CS_HREDRAW | windows.CS_VREDRAW | windows.CS_OWNDC, LpfnWndProc: syscall.NewCallback(windowProc), HInstance: hInst, - HCursor: curs, + HIcon: icon, LpszClassName: syscall.StringToUTF16Ptr("GioWindow"), } cls, err := windows.RegisterClassEx(&wcls) @@ -139,16 +151,7 @@ func initResources() error { return nil } -func getWindowConstraints(cfg unit.Metric, opts *Options, d winDeltas) winConstraints { - var minmax winConstraints - minmax.minWidth = int32(cfg.Px(opts.MinWidth)) - minmax.minHeight = int32(cfg.Px(opts.MinHeight)) - minmax.maxWidth = int32(cfg.Px(opts.MaxWidth)) - minmax.maxHeight = int32(cfg.Px(opts.MaxHeight)) - return minmax -} - -func createNativeWindow(opts *Options) (*window, error) { +func createNativeWindow() (*window, error) { var resErr error resources.once.Do(func() { resErr = initResources() @@ -156,28 +159,15 @@ func createNativeWindow(opts *Options) (*window, error) { if resErr != nil { return nil, resErr } - cfg := configForDC() - wr := windows.Rect{ - Right: int32(cfg.Px(opts.Width)), - Bottom: int32(cfg.Px(opts.Height)), - } dwStyle := uint32(windows.WS_OVERLAPPEDWINDOW) dwExStyle := uint32(windows.WS_EX_APPWINDOW | windows.WS_EX_WINDOWEDGE) - deltas := winDeltas{ - width: wr.Right, - height: wr.Bottom, - } - windows.AdjustWindowRectEx(&wr, dwStyle, 0, dwExStyle) - deltas.width = wr.Right - wr.Left - deltas.width - deltas.height = wr.Bottom - wr.Top - deltas.height hwnd, err := windows.CreateWindowEx(dwExStyle, resources.class, - opts.Title, + "", dwStyle|windows.WS_CLIPSIBLINGS|windows.WS_CLIPCHILDREN, windows.CW_USEDEFAULT, windows.CW_USEDEFAULT, - wr.Right-wr.Left, - wr.Bottom-wr.Top, + windows.CW_USEDEFAULT, windows.CW_USEDEFAULT, 0, 0, resources.handle, @@ -186,10 +176,7 @@ func createNativeWindow(opts *Options) (*window, error) { return nil, err } w := &window{ - hwnd: hwnd, - minmax: getWindowConstraints(cfg, opts, deltas), - deltas: deltas, - opts: opts, + hwnd: hwnd, } w.hdc, err = windows.GetDC(hwnd) if err != nil { @@ -210,7 +197,7 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr case windows.WM_UNICHAR: if wParam == windows.UNICODE_NOCHAR { // Tell the system that we accept WM_UNICHAR messages. - return 1 + return windows.TRUE } fallthrough case windows.WM_CHAR: @@ -218,29 +205,44 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr w.w.Event(key.EditEvent{Text: string(r)}) } // The message is processed. - return 1 + return windows.TRUE case windows.WM_DPICHANGED: // Let Windows know we're prepared for runtime DPI changes. - return 1 + return windows.TRUE case windows.WM_ERASEBKGND: // Avoid flickering between GPU content and background color. - return 1 - case windows.WM_KEYDOWN, windows.WM_SYSKEYDOWN: + return windows.TRUE + case windows.WM_KEYDOWN, windows.WM_KEYUP, windows.WM_SYSKEYDOWN, windows.WM_SYSKEYUP: if n, ok := convertKeyCode(wParam); ok { - w.w.Event(key.Event{Name: n, Modifiers: getModifiers()}) + e := key.Event{ + Name: n, + Modifiers: getModifiers(), + State: key.Press, + } + if msg == windows.WM_KEYUP || msg == windows.WM_SYSKEYUP { + e.State = key.Release + } + + w.w.Event(e) + + if (wParam == windows.VK_F10) && (msg == windows.WM_SYSKEYDOWN || msg == windows.WM_SYSKEYUP) { + // Reserve F10 for ourselves, and don't let it open the system menu. Other Windows programs + // such as cmd.exe and graphical debuggers also reserve F10. + return 0 + } } case windows.WM_LBUTTONDOWN: - w.pointerButton(pointer.ButtonLeft, true, lParam, getModifiers()) + w.pointerButton(pointer.ButtonPrimary, true, lParam, getModifiers()) case windows.WM_LBUTTONUP: - w.pointerButton(pointer.ButtonLeft, false, lParam, getModifiers()) + w.pointerButton(pointer.ButtonPrimary, false, lParam, getModifiers()) case windows.WM_RBUTTONDOWN: - w.pointerButton(pointer.ButtonRight, true, lParam, getModifiers()) + w.pointerButton(pointer.ButtonSecondary, true, lParam, getModifiers()) case windows.WM_RBUTTONUP: - w.pointerButton(pointer.ButtonRight, false, lParam, getModifiers()) + w.pointerButton(pointer.ButtonSecondary, false, lParam, getModifiers()) case windows.WM_MBUTTONDOWN: - w.pointerButton(pointer.ButtonMiddle, true, lParam, getModifiers()) + w.pointerButton(pointer.ButtonTertiary, true, lParam, getModifiers()) case windows.WM_MBUTTONUP: - w.pointerButton(pointer.ButtonMiddle, false, lParam, getModifiers()) + w.pointerButton(pointer.ButtonTertiary, false, lParam, getModifiers()) case windows.WM_CANCELMODE: w.w.Event(pointer.Event{ Type: pointer.Cancel, @@ -260,9 +262,19 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr Time: windows.GetMessageTime(), }) case windows.WM_MOUSEWHEEL: - w.scrollEvent(wParam, lParam) + w.scrollEvent(wParam, lParam, false) + case windows.WM_MOUSEHWHEEL: + w.scrollEvent(wParam, lParam, true) case windows.WM_DESTROY: - w.dead = true + w.w.Event(ViewEvent{}) + w.w.Event(system.DestroyEvent{}) + if w.hdc != 0 { + windows.ReleaseDC(w.hdc) + w.hdc = 0 + } + // The system destroys the HWND for us. + w.hwnd = 0 + windows.PostQuitMessage(0) case windows.WM_PAINT: w.draw(true) case windows.WM_SIZE: @@ -270,22 +282,51 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr case windows.SIZE_MINIMIZED: w.setStage(system.StagePaused) case windows.SIZE_MAXIMIZED, windows.SIZE_RESTORED: + var triggerEvent bool + // Check the window size change. + var r windows.Rect + windows.GetClientRect(w.hwnd, &r) + size := image.Point{ + X: int(r.Right - r.Left), + Y: int(r.Bottom - r.Top), + } + if size != w.config.Size { + w.config.Size = size + triggerEvent = true + } + // Check the window mode. + mode := w.config.Mode + w.updateWindowMode() + if mode != w.config.Mode { + triggerEvent = true + } + if triggerEvent { + w.w.Event(ConfigEvent{Config: w.config}) + } w.setStage(system.StageRunning) } case windows.WM_GETMINMAXINFO: mm := (*windows.MinMaxInfo)(unsafe.Pointer(uintptr(lParam))) - if w.minmax.minWidth > 0 || w.minmax.minHeight > 0 { + if p := w.config.MinSize; p.X > 0 || p.Y > 0 { mm.PtMinTrackSize = windows.Point{ - w.minmax.minWidth+w.deltas.width, - w.minmax.minHeight+w.deltas.height, + X: int32(p.X) + w.deltas.width, + Y: int32(p.Y) + w.deltas.height, } } - if w.minmax.maxWidth > 0 || w.minmax.maxHeight > 0 { + if p := w.config.MaxSize; p.X > 0 || p.Y > 0 { mm.PtMaxTrackSize = windows.Point{ - w.minmax.maxWidth+w.deltas.width, - w.minmax.maxHeight+w.deltas.height, + X: int32(p.X) + w.deltas.width, + Y: int32(p.Y) + w.deltas.height, } } + case windows.WM_SETCURSOR: + w.cursorIn = (lParam & 0xffff) == windows.HTCLIENT + if w.cursorIn { + windows.SetCursor(w.cursor) + return windows.TRUE + } + case _WM_WAKEUP: + w.w.Event(wakeupEvent{}) } return windows.DefWindowProc(hwnd, msg, wParam, lParam) @@ -341,7 +382,7 @@ func coordsFromlParam(lParam uintptr) (int, int) { return x, y } -func (w *window) scrollEvent(wParam, lParam uintptr) { +func (w *window) scrollEvent(wParam, lParam uintptr, horizontal bool) { x, y := coordsFromlParam(lParam) // The WM_MOUSEWHEEL coordinates are in screen coordinates, in contrast // to other mouse events. @@ -349,12 +390,18 @@ func (w *window) scrollEvent(wParam, lParam uintptr) { windows.ScreenToClient(w.hwnd, &np) p := f32.Point{X: float32(np.X), Y: float32(np.Y)} dist := float32(int16(wParam >> 16)) + var sp f32.Point + if horizontal { + sp.X = dist + } else { + sp.Y = -dist + } w.w.Event(pointer.Event{ Type: pointer.Scroll, Source: pointer.Mouse, Position: p, Buttons: w.pointerBtns, - Scroll: f32.Point{Y: -dist}, + Scroll: sp, Time: windows.GetMessageTime(), }) } @@ -362,18 +409,19 @@ func (w *window) scrollEvent(wParam, lParam uintptr) { // Adapted from https://blogs.msdn.microsoft.com/oldnewthing/20060126-00/?p=32513/ func (w *window) loop() error { msg := new(windows.Msg) - for !w.dead { - w.mu.Lock() +loop: + for { anim := w.animating - w.mu.Unlock() - if anim && !windows.PeekMessage(msg, w.hwnd, 0, 0, windows.PM_NOREMOVE) { + if anim && !windows.PeekMessage(msg, 0, 0, 0, windows.PM_NOREMOVE) { w.draw(false) continue } - windows.GetMessage(msg, w.hwnd, 0, 0) - if msg.Message == windows.WM_QUIT { - windows.PostQuitMessage(msg.WParam) - break + switch ret := windows.GetMessage(msg, 0, 0, 0); ret { + case -1: + return errors.New("GetMessage failed") + case 0: + // WM_QUIT received. + break loop } windows.TranslateMessage(msg) windows.DispatchMessage(msg) @@ -382,65 +430,44 @@ func (w *window) loop() error { } func (w *window) SetAnimating(anim bool) { - w.mu.Lock() w.animating = anim - w.mu.Unlock() - if anim { - w.postRedraw() - } } -func (w *window) postRedraw() { - if err := windows.PostMessage(w.hwnd, _WM_REDRAW, 0, 0); err != nil { +func (w *window) Wakeup() { + if err := windows.PostMessage(w.hwnd, _WM_WAKEUP, 0, 0); err != nil { panic(err) } } func (w *window) setStage(s system.Stage) { - w.stage = s - w.w.Event(system.StageEvent{Stage: s}) + if s != w.stage { + w.stage = s + w.w.Event(system.StageEvent{Stage: s}) + } } func (w *window) draw(sync bool) { - var r windows.Rect - windows.GetClientRect(w.hwnd, &r) - w.width = int(r.Right - r.Left) - w.height = int(r.Bottom - r.Top) - if w.width == 0 || w.height == 0 { + if w.config.Size.X == 0 || w.config.Size.Y == 0 { return } - cfg := configForDC() - w.minmax = getWindowConstraints(cfg, w.opts, w.deltas) - w.w.Event(FrameEvent{ + dpi := windows.GetWindowDPI(w.hwnd) + cfg := configForDPI(dpi) + w.w.Event(frameEvent{ FrameEvent: system.FrameEvent{ - Now: time.Now(), - Size: image.Point{ - X: w.width, - Y: w.height, - }, + Now: time.Now(), + Size: w.config.Size, Metric: cfg, }, Sync: sync, }) } -func (w *window) destroy() { - if w.hdc != 0 { - windows.ReleaseDC(w.hdc) - w.hdc = 0 - } - if w.hwnd != 0 { - windows.DestroyWindow(w.hwnd) - w.hwnd = 0 - } -} - -func (w *window) NewContext() (Context, error) { - sort.Slice(backends, func(i, j int) bool { - return backends[i].priority < backends[j].priority +func (w *window) NewContext() (context, error) { + sort.Slice(drivers, func(i, j int) bool { + return drivers[i].priority < drivers[j].priority }) var errs []string - for _, b := range backends { + for _, b := range drivers { ctx, err := b.initializer(w) if err == nil { return ctx, nil @@ -450,7 +477,7 @@ func (w *window) NewContext() (Context, error) { if len(errs) > 0 { return nil, fmt.Errorf("NewContext: failed to create a GPU device, tried: %s", strings.Join(errs, ", ")) } - return nil, errors.New("NewContext: no available backends") + return nil, errors.New("NewContext: no available GPU drivers") } func (w *window) ReadClipboard() { @@ -471,25 +498,142 @@ func (w *window) readClipboard() error { return err } defer windows.GlobalUnlock(mem) - // Look for terminating null character. - n := 0 - for { - ch := *(*uint16)(unsafe.Pointer(ptr + uintptr(n)*2)) - if ch == 0 { - break + content := gowindows.UTF16PtrToString((*uint16)(unsafe.Pointer(ptr))) + w.w.Event(clipboard.Event{Text: content}) + return nil +} + +func (w *window) updateWindowMode() { + p := windows.GetWindowPlacement(w.hwnd) + r := p.Rect() + mi := windows.GetMonitorInfo(w.hwnd) + if r == mi.Monitor { + w.config.Mode = Fullscreen + } else { + w.config.Mode = Windowed + } +} + +func (w *window) Configure(options []Option) { + dpi := windows.GetSystemDPI() + cfg := configForDPI(dpi) + prev := w.config + w.updateWindowMode() + cnf := w.config + cnf.apply(cfg, options) + + if cnf.Mode != Fullscreen && prev.Size != cnf.Size { + width := int32(cnf.Size.X) + height := int32(cnf.Size.Y) + w.config.Size = cnf.Size + + // Include the window decorations. + wr := windows.Rect{ + Right: width, + Bottom: height, } - n++ + dwStyle := uint32(windows.WS_OVERLAPPEDWINDOW) + dwExStyle := uint32(windows.WS_EX_APPWINDOW | windows.WS_EX_WINDOWEDGE) + windows.AdjustWindowRectEx(&wr, dwStyle, 0, dwExStyle) + + dw, dh := width, height + width = wr.Right - wr.Left + height = wr.Bottom - wr.Top + w.deltas.width = width - dw + w.deltas.height = height - dh + + windows.MoveWindow(w.hwnd, 0, 0, width, height, true) + } + if prev.MinSize != cnf.MinSize { + w.config.MinSize = cnf.MinSize + } + if prev.MaxSize != cnf.MaxSize { + w.config.MaxSize = cnf.MaxSize + } + if prev.Title != cnf.Title { + w.config.Title = cnf.Title + windows.SetWindowText(w.hwnd, cnf.Title) + } + if prev.Mode != cnf.Mode { + w.SetWindowMode(cnf.Mode) + } + if w.config != prev { + w.w.Event(ConfigEvent{Config: w.config}) + } +} + +// Maximize the window. It will have no effect when in fullscreen mode. +func (w *window) Maximize() { + if w.config.Mode == Fullscreen { + return + } + style := windows.GetWindowLong(w.hwnd) + windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style|windows.WS_OVERLAPPEDWINDOW|windows.WS_MAXIMIZE) + mi := windows.GetMonitorInfo(w.hwnd) + windows.SetWindowPos(w.hwnd, 0, + mi.Monitor.Left, mi.Monitor.Top, + mi.Monitor.Right-mi.Monitor.Left, + mi.Monitor.Bottom-mi.Monitor.Top, + windows.SWP_NOOWNERZORDER|windows.SWP_FRAMECHANGED, + ) +} + +// Center will place window at monitor center. +func (w *window) Center() { + // Make sure that the window is sizeable + style := windows.GetWindowLong(w.hwnd) & (^uintptr(windows.WS_MAXIMIZE)) + windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style|windows.WS_OVERLAPPEDWINDOW) + + // Find with/height including the window decorations. + wr := windows.Rect{ + Right: int32(w.config.Size.X), + Bottom: int32(w.config.Size.Y), + } + dwStyle := uint32(windows.WS_OVERLAPPEDWINDOW) + dwExStyle := uint32(windows.WS_EX_APPWINDOW | windows.WS_EX_WINDOWEDGE) + windows.AdjustWindowRectEx(&wr, dwStyle, 0, dwExStyle) + width := wr.Right - wr.Left + height := wr.Bottom - wr.Top + + // Move to center of current monitor + mi := windows.GetMonitorInfo(w.hwnd).Monitor + x := mi.Left + (mi.Right-mi.Left-width)/2 + y := mi.Top + (mi.Bottom-mi.Top-height)/2 + windows.MoveWindow(w.hwnd, x, y, width, height, true) +} + +func (w *window) SetWindowMode(mode WindowMode) { + // https://devblogs.microsoft.com/oldnewthing/20100412-00/?p=14353 + switch mode { + case Windowed: + if w.placement == nil { + return + } + w.config.Mode = Windowed + windows.SetWindowPlacement(w.hwnd, w.placement) + w.placement = nil + style := windows.GetWindowLong(w.hwnd) + windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style|windows.WS_OVERLAPPEDWINDOW) + windows.SetWindowPos(w.hwnd, windows.HWND_TOPMOST, + 0, 0, 0, 0, + windows.SWP_NOOWNERZORDER|windows.SWP_FRAMECHANGED, + ) + case Fullscreen: + if w.placement != nil { + return + } + w.config.Mode = Fullscreen + w.placement = windows.GetWindowPlacement(w.hwnd) + style := windows.GetWindowLong(w.hwnd) + windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style&^windows.WS_OVERLAPPEDWINDOW) + mi := windows.GetMonitorInfo(w.hwnd) + windows.SetWindowPos(w.hwnd, 0, + mi.Monitor.Left, mi.Monitor.Top, + mi.Monitor.Right-mi.Monitor.Left, + mi.Monitor.Bottom-mi.Monitor.Top, + windows.SWP_NOOWNERZORDER|windows.SWP_FRAMECHANGED, + ) } - var u16 []uint16 - hdr := (*reflect.SliceHeader)(unsafe.Pointer(&u16)) - hdr.Data = ptr - hdr.Cap = n - hdr.Len = n - content := string(utf16.Decode(u16)) - go func() { - w.w.Event(system.ClipboardEvent{Text: content}) - }() - return nil } func (w *window) WriteClipboard(s string) { @@ -497,9 +641,6 @@ func (w *window) WriteClipboard(s string) { } func (w *window) writeClipboard(s string) error { - u16 := utf16.Encode([]rune(s)) - // Data must be null terminated. - u16 = append(u16, 0) if err := windows.OpenClipboard(w.hwnd); err != nil { return err } @@ -507,6 +648,10 @@ func (w *window) writeClipboard(s string) error { if err := windows.EmptyClipboard(); err != nil { return err } + u16, err := gowindows.UTF16FromString(s) + if err != nil { + return err + } n := len(u16) * int(unsafe.Sizeof(u16[0])) mem, err := windows.GlobalAlloc(n) if err != nil { @@ -531,20 +676,64 @@ func (w *window) writeClipboard(s string) error { return nil } +func (w *window) SetCursor(name pointer.CursorName) { + c, err := loadCursor(name) + if err != nil { + c = resources.cursor + } + w.cursor = c + if w.cursorIn { + windows.SetCursor(w.cursor) + } +} + +func loadCursor(name pointer.CursorName) (syscall.Handle, error) { + var curID uint16 + switch name { + default: + fallthrough + case pointer.CursorDefault: + return resources.cursor, nil + case pointer.CursorText: + curID = windows.IDC_IBEAM + case pointer.CursorPointer: + curID = windows.IDC_HAND + case pointer.CursorCrossHair: + curID = windows.IDC_CROSS + case pointer.CursorColResize: + curID = windows.IDC_SIZEWE + case pointer.CursorRowResize: + curID = windows.IDC_SIZENS + case pointer.CursorGrab: + curID = windows.IDC_SIZEALL + case pointer.CursorNone: + return 0, nil + } + return windows.LoadCursor(curID) +} + func (w *window) ShowTextInput(show bool) {} +func (w *window) SetInputHint(_ key.InputHint) {} + func (w *window) HDC() syscall.Handle { return w.hdc } func (w *window) HWND() (syscall.Handle, int, int) { - return w.hwnd, w.width, w.height + return w.hwnd, w.config.Size.X, w.config.Size.Y } func (w *window) Close() { windows.PostMessage(w.hwnd, windows.WM_CLOSE, 0, 0) } +func (w *window) Raise() { + windows.SetForegroundWindow(w.hwnd) + windows.SetWindowPos(w.hwnd, windows.HWND_TOPMOST, 0, 0, 0, 0, + windows.SWP_NOMOVE|windows.SWP_NOSIZE|windows.SWP_SHOWWINDOW) +} + func convertKeyCode(code uintptr) (string, bool) { if '0' <= code && code <= '9' || 'A' <= code && code <= 'Z' { return string(rune(code)), true @@ -602,7 +791,7 @@ func convertKeyCode(code uintptr) (string, bool) { case windows.VK_TAB: r = key.NameTab case windows.VK_SPACE: - r = "Space" + r = key.NameSpace case windows.VK_OEM_1: r = ";" case windows.VK_OEM_PLUS: @@ -631,8 +820,7 @@ func convertKeyCode(code uintptr) (string, bool) { return r, true } -func configForDC() unit.Metric { - dpi := windows.GetSystemDPI() +func configForDPI(dpi int) unit.Metric { const inchPrDp = 1.0 / 96.0 ppdp := float32(dpi) * inchPrDp return unit.Metric{ @@ -640,3 +828,5 @@ func configForDC() unit.Metric { PxPerSp: ppdp, } } + +func (_ ViewEvent) ImplementsEvent() {} diff --git a/vendor/gioui.org/app/internal/window/os_x11.go b/vendor/gioui.org/app/os_x11.go index 69108c6..1c75247 100644 --- a/vendor/gioui.org/app/internal/window/os_x11.go +++ b/vendor/gioui.org/app/os_x11.go @@ -1,14 +1,16 @@ // SPDX-License-Identifier: Unlicense OR MIT -// +build linux,!android,!nox11 freebsd openbsd +//go:build ((linux && !android) || freebsd || openbsd) && !nox11 +// +build linux,!android freebsd openbsd +// +build !nox11 -package window +package app /* -#cgo openbsd CFLAGS: -I/usr/X11R6/include -I/usr/local/include -#cgo openbsd LDFLAGS: -L/usr/X11R6/lib -L/usr/local/lib -#cgo freebsd openbsd LDFLAGS: -lX11 -lxkbcommon -lxkbcommon-x11 -lX11-xcb -#cgo linux pkg-config: x11 xkbcommon xkbcommon-x11 x11-xcb +#cgo freebsd openbsd CFLAGS: -I/usr/X11R6/include -I/usr/local/include +#cgo freebsd openbsd LDFLAGS: -L/usr/X11R6/lib -L/usr/local/lib +#cgo freebsd openbsd LDFLAGS: -lX11 -lxkbcommon -lxkbcommon-x11 -lX11-xcb -lXcursor -lXfixes +#cgo linux pkg-config: x11 xkbcommon xkbcommon-x11 x11-xcb xcursor xfixes #include <stdlib.h> #include <locale.h> @@ -18,6 +20,8 @@ package window #include <X11/Xresource.h> #include <X11/XKBlib.h> #include <X11/Xlib-xcb.h> +#include <X11/extensions/Xfixes.h> +#include <X11/Xcursor/Xcursor.h> #include <xkbcommon/xkbcommon-x11.h> */ @@ -34,17 +38,24 @@ import ( "unsafe" "gioui.org/f32" + "gioui.org/io/clipboard" "gioui.org/io/key" "gioui.org/io/pointer" "gioui.org/io/system" "gioui.org/unit" - "gioui.org/app/internal/xkb" syscall "golang.org/x/sys/unix" + + "gioui.org/app/internal/xkb" +) + +const ( + _NET_WM_STATE_REMOVE = 0 + _NET_WM_STATE_ADD = 1 ) type x11Window struct { - w Callbacks + w *callbacks x *C.Display xkb *xkb.Context xkbEventBase C.int @@ -53,68 +64,213 @@ type x11Window struct { atoms struct { // "UTF8_STRING". utf8string C.Atom + // "text/plain;charset=utf-8". + plaintext C.Atom // "TARGETS" targets C.Atom // "CLIPBOARD". clipboard C.Atom + // "PRIMARY". + primary C.Atom // "CLIPBOARD_CONTENT", the clipboard destination property. clipboardContent C.Atom // "WM_DELETE_WINDOW" evDelWindow C.Atom // "ATOM" atom C.Atom + // "GTK_TEXT_BUFFER_CONTENTS" + gtk_text_buffer_contents C.Atom + // "_NET_WM_NAME" + wmName C.Atom + // "_NET_WM_STATE" + wmState C.Atom + // "_NET_WM_STATE_FULLSCREEN" + wmStateFullscreen C.Atom + // "_NET_ACTIVE_WINDOW" + wmActiveWindow C.Atom + // _NET_WM_STATE_MAXIMIZED_HORZ + wmStateMaximizedHorz C.Atom + // _NET_WM_STATE_MAXIMIZED_VERT + wmStateMaximizedVert C.Atom } stage system.Stage - cfg unit.Metric - width int - height int + metric unit.Metric notify struct { read, write int } dead bool - mu sync.Mutex animating bool pointerBtns pointer.Buttons clipboard struct { - read bool - write *string content []byte } + cursor pointer.CursorName + config Config + + wakeups chan struct{} +} + +var ( + newX11EGLContext func(w *x11Window) (context, error) + newX11VulkanContext func(w *x11Window) (context, error) +) + +func (w *x11Window) NewContext() (context, error) { + var firstErr error + if f := newX11VulkanContext; f != nil { + c, err := f(w) + if err == nil { + return c, nil + } + firstErr = err + } + if f := newX11EGLContext; f != nil { + c, err := f(w) + if err == nil { + return c, nil + } + firstErr = err + } + if firstErr != nil { + return nil, firstErr + } + return nil, errors.New("x11: no available GPU backends") } func (w *x11Window) SetAnimating(anim bool) { - w.mu.Lock() w.animating = anim - w.mu.Unlock() - if anim { - w.wakeup() - } } func (w *x11Window) ReadClipboard() { - w.mu.Lock() - w.clipboard.read = true - w.mu.Unlock() - w.wakeup() + C.XDeleteProperty(w.x, w.xw, w.atoms.clipboardContent) + C.XConvertSelection(w.x, w.atoms.clipboard, w.atoms.utf8string, w.atoms.clipboardContent, w.xw, C.CurrentTime) } func (w *x11Window) WriteClipboard(s string) { - w.mu.Lock() - w.clipboard.write = &s - w.mu.Unlock() - w.wakeup() + w.clipboard.content = []byte(s) + C.XSetSelectionOwner(w.x, w.atoms.clipboard, w.xw, C.CurrentTime) + C.XSetSelectionOwner(w.x, w.atoms.primary, w.xw, C.CurrentTime) +} + +func (w *x11Window) Configure(options []Option) { + var shints C.XSizeHints + prev := w.config + cnf := w.config + cnf.apply(w.metric, options) + if prev.MinSize != cnf.MinSize { + w.config.MinSize = cnf.MinSize + shints.min_width = C.int(cnf.MinSize.X) + shints.min_height = C.int(cnf.MinSize.Y) + shints.flags = C.PMinSize + } + if prev.MaxSize != cnf.MaxSize { + w.config.MaxSize = cnf.MaxSize + shints.max_width = C.int(cnf.MaxSize.X) + shints.max_height = C.int(cnf.MaxSize.Y) + shints.flags = shints.flags | C.PMaxSize + } + if shints.flags != 0 { + C.XSetWMNormalHints(w.x, w.xw, &shints) + } + + if cnf.Mode != Fullscreen && prev.Size != cnf.Size { + w.config.Size = cnf.Size + C.XResizeWindow(w.x, w.xw, C.uint(cnf.Size.X), C.uint(cnf.Size.Y)) + } + + if prev.Title != cnf.Title { + title := cnf.Title + ctitle := C.CString(title) + defer C.free(unsafe.Pointer(ctitle)) + C.XStoreName(w.x, w.xw, ctitle) + // set _NET_WM_NAME as well for UTF-8 support in window title. + C.XSetTextProperty(w.x, w.xw, + &C.XTextProperty{ + value: (*C.uchar)(unsafe.Pointer(ctitle)), + encoding: w.atoms.utf8string, + format: 8, + nitems: C.ulong(len(title)), + }, + w.atoms.wmName) + } + + if prev.Mode != cnf.Mode { + w.SetWindowMode(cnf.Mode) + } + if w.config != prev { + w.w.Event(ConfigEvent{Config: w.config}) + } +} + +func (w *x11Window) Raise() { + var xev C.XEvent + ev := (*C.XClientMessageEvent)(unsafe.Pointer(&xev)) + *ev = C.XClientMessageEvent{ + _type: C.ClientMessage, + display: w.x, + window: w.xw, + message_type: w.atoms.wmActiveWindow, + format: 32, + } + C.XSendEvent( + w.x, + C.XDefaultRootWindow(w.x), // MUST be the root window + C.False, + C.SubstructureNotifyMask|C.SubstructureRedirectMask, + &xev, + ) + C.XMapRaised(w.display(), w.xw) +} + +func (w *x11Window) SetCursor(name pointer.CursorName) { + switch name { + case pointer.CursorNone: + w.cursor = name + C.XFixesHideCursor(w.x, w.xw) + return + case pointer.CursorGrab: + name = "hand1" + } + if w.cursor == pointer.CursorNone { + C.XFixesShowCursor(w.x, w.xw) + } + cname := C.CString(string(name)) + defer C.free(unsafe.Pointer(cname)) + c := C.XcursorLibraryLoadCursor(w.x, cname) + if c == 0 { + name = pointer.CursorDefault + } + w.cursor = name + // If c if null (i.e. name was not found), + // XDefineCursor will use the default cursor. + C.XDefineCursor(w.x, w.xw, c) +} + +func (w *x11Window) SetWindowMode(mode WindowMode) { + var action C.long + switch mode { + case Windowed: + action = _NET_WM_STATE_REMOVE + case Fullscreen: + action = _NET_WM_STATE_ADD + default: + return + } + w.config.Mode = mode + // "A Client wishing to change the state of a window MUST send + // a _NET_WM_STATE client message to the root window." + w.sendWMStateEvent(action, w.atoms.wmStateFullscreen, 0) } func (w *x11Window) ShowTextInput(show bool) {} +func (w *x11Window) SetInputHint(_ key.InputHint) {} + // Close the window. func (w *x11Window) Close() { - w.mu.Lock() - defer w.mu.Unlock() - var xev C.XEvent ev := (*C.XClientMessageEvent)(unsafe.Pointer(&xev)) *ev = C.XClientMessageEvent{ @@ -130,9 +286,62 @@ func (w *x11Window) Close() { C.XSendEvent(w.x, w.xw, C.False, C.NoEventMask, &xev) } +// Maximize the window. +func (w *x11Window) Maximize() { + w.sendWMStateEvent(_NET_WM_STATE_ADD, w.atoms.wmStateMaximizedHorz, w.atoms.wmStateMaximizedVert) +} + +// Center the window. +func (w *x11Window) Center() { + screen := C.XDefaultScreen(w.x) + width := C.XDisplayWidth(w.x, screen) + height := C.XDisplayHeight(w.x, screen) + + var attrs C.XWindowAttributes + C.XGetWindowAttributes(w.x, w.xw, &attrs) + width -= attrs.border_width + height -= attrs.border_width + + sz := w.config.Size + x := (int(width) - sz.X) / 2 + y := (int(height) - sz.Y) / 2 + + C.XMoveResizeWindow(w.x, w.xw, C.int(x), C.int(y), C.uint(sz.X), C.uint(sz.Y)) +} + +// action is one of _NET_WM_STATE_REMOVE, _NET_WM_STATE_ADD. +func (w *x11Window) sendWMStateEvent(action C.long, atom1, atom2 C.ulong) { + var xev C.XEvent + ev := (*C.XClientMessageEvent)(unsafe.Pointer(&xev)) + *ev = C.XClientMessageEvent{ + _type: C.ClientMessage, + display: w.x, + window: w.xw, + message_type: w.atoms.wmState, + format: 32, + } + data := (*[5]C.long)(unsafe.Pointer(&ev.data)) + data[0] = C.long(action) + data[1] = C.long(atom1) + data[2] = C.long(atom2) + data[3] = 1 // application + + C.XSendEvent( + w.x, + C.XDefaultRootWindow(w.x), // MUST be the root window + C.False, + C.SubstructureNotifyMask|C.SubstructureRedirectMask, + &xev, + ) +} + var x11OneByte = make([]byte, 1) -func (w *x11Window) wakeup() { +func (w *x11Window) Wakeup() { + select { + case w.wakeups <- struct{}{}: + default: + } if _, err := syscall.Write(w.notify.write, x11OneByte); err != nil && err != syscall.EAGAIN { panic(fmt.Errorf("failed to write to pipe: %v", err)) } @@ -143,7 +352,7 @@ func (w *x11Window) display() *C.Display { } func (w *x11Window) window() (C.Window, int, int) { - return w.xw, w.width, w.height + return w.xw, w.config.Size.X, w.config.Size.Y } func (w *x11Window) setStage(s system.Stage) { @@ -151,7 +360,7 @@ func (w *x11Window) setStage(s system.Stage) { return } w.stage = s - w.w.Event(system.StageEvent{s}) + w.w.Event(system.StageEvent{Stage: s}) } func (w *x11Window) loop() { @@ -174,9 +383,7 @@ loop: // This fixes an issue on Xephyr where on startup XPending() > 0 but // poll will still block. This also prevents no-op calls to poll. if syn = h.handleEvents(); !syn { - w.mu.Lock() anim = w.animating - w.mu.Unlock() if !anim { // Clear poll events. *xEvents = 0 @@ -205,34 +412,22 @@ loop: panic(fmt.Errorf("x11 loop: read from notify pipe failed: %w", err)) } } + select { + case <-w.wakeups: + w.w.Event(wakeupEvent{}) + default: + } - if anim || syn { - w.w.Event(FrameEvent{ + if (anim || syn) && w.config.Size.X != 0 && w.config.Size.Y != 0 { + w.w.Event(frameEvent{ FrameEvent: system.FrameEvent{ - Now: time.Now(), - Size: image.Point{ - X: w.width, - Y: w.height, - }, - Metric: w.cfg, + Now: time.Now(), + Size: w.config.Size, + Metric: w.metric, }, Sync: syn, }) } - w.mu.Lock() - readClipboard := w.clipboard.read - writeClipboard := w.clipboard.write - w.clipboard.read = false - w.clipboard.write = nil - w.mu.Unlock() - if readClipboard { - C.XDeleteProperty(w.x, w.xw, w.atoms.clipboardContent) - C.XConvertSelection(w.x, w.atoms.clipboard, w.atoms.utf8string, w.atoms.clipboardContent, w.xw, C.CurrentTime) - } - if writeClipboard != nil { - w.clipboard.content = []byte(*writeClipboard) - C.XSetSelectionOwner(w.x, w.atoms.clipboard, w.xw, C.CurrentTime) - } } w.w.Event(system.DestroyEvent{Err: nil}) } @@ -301,12 +496,15 @@ func (h *x11EventHandler) handleEvents() bool { h.w.xkb.UpdateMask(uint32(state.base_mods), uint32(state.latched_mods), uint32(state.locked_mods), uint32(state.base_group), uint32(state.latched_group), uint32(state.locked_group)) } - case C.KeyPress: + case C.KeyPress, C.KeyRelease: + ks := key.Press + if _type == C.KeyRelease { + ks = key.Release + } kevt := (*C.XKeyPressedEvent)(unsafe.Pointer(xev)) - for _, e := range h.w.xkb.DispatchKey(uint32(kevt.keycode)) { + for _, e := range h.w.xkb.DispatchKey(uint32(kevt.keycode), ks) { w.w.Event(e) } - case C.KeyRelease: case C.ButtonPress, C.ButtonRelease: bevt := (*C.XButtonEvent)(unsafe.Pointer(xev)) ev := pointer.Event{ @@ -326,11 +524,11 @@ func (h *x11EventHandler) handleEvents() bool { const scrollScale = 10 switch bevt.button { case C.Button1: - btn = pointer.ButtonLeft + btn = pointer.ButtonPrimary case C.Button2: - btn = pointer.ButtonMiddle + btn = pointer.ButtonTertiary case C.Button3: - btn = pointer.ButtonRight + btn = pointer.ButtonSecondary case C.Button4: // scroll up ev.Type = pointer.Scroll @@ -339,6 +537,15 @@ func (h *x11EventHandler) handleEvents() bool { // scroll down ev.Type = pointer.Scroll ev.Scroll.Y = +scrollScale + case 6: + // http://xahlee.info/linux/linux_x11_mouse_button_number.html + // scroll left + ev.Type = pointer.Scroll + ev.Scroll.X = -scrollScale * 2 + case 7: + // scroll right + ev.Type = pointer.Scroll + ev.Scroll.X = +scrollScale * 2 default: continue } @@ -372,8 +579,10 @@ func (h *x11EventHandler) handleEvents() bool { w.w.Event(key.FocusEvent{Focus: false}) case C.ConfigureNotify: // window configuration change cevt := (*C.XConfigureEvent)(unsafe.Pointer(xev)) - w.width = int(cevt.width) - w.height = int(cevt.height) + if sz := image.Pt(int(cevt.width), int(cevt.height)); sz != w.config.Size { + w.config.Size = sz + w.w.Event(ConfigEvent{Config: w.config}) + } // redraw will be done by a later expose event case C.SelectionNotify: cevt := (*C.XSelectionEvent)(unsafe.Pointer(xev)) @@ -394,10 +603,10 @@ func (h *x11EventHandler) handleEvents() bool { break } str := C.GoStringN((*C.char)(unsafe.Pointer(text.value)), C.int(text.nitems)) - w.w.Event(system.ClipboardEvent{Text: str}) + w.w.Event(clipboard.Event{Text: str}) case C.SelectionRequest: cevt := (*C.XSelectionRequestEvent)(unsafe.Pointer(xev)) - if cevt.selection != w.atoms.clipboard || cevt.property == C.None { + if (cevt.selection != w.atoms.clipboard && cevt.selection != w.atoms.primary) || cevt.property == C.None { // Unsupported clipboard or obsolete requestor. break } @@ -418,21 +627,27 @@ func (h *x11EventHandler) handleEvents() bool { switch cevt.target { case w.atoms.targets: // The requestor wants the supported clipboard - // formats. First write the formats... - formats := []uint32{uint32(w.atoms.utf8string)} + // formats. First write the targets... + formats := [...]C.long{ + C.long(w.atoms.targets), + C.long(w.atoms.utf8string), + C.long(w.atoms.plaintext), + // GTK clients need this. + C.long(w.atoms.gtk_text_buffer_contents), + } C.XChangeProperty(w.x, cevt.requestor, cevt.property, w.atoms.atom, 32 /* bitwidth of formats */, C.PropModeReplace, - (*C.uchar)(unsafe.Pointer(&formats[0])), C.int(len(formats)), + (*C.uchar)(unsafe.Pointer(&formats)), C.int(len(formats)), ) // ...then notify the requestor. notify() - case w.atoms.utf8string: + case w.atoms.plaintext, w.atoms.utf8string, w.atoms.gtk_text_buffer_contents: content := w.clipboard.content var ptr *C.uchar if len(content) > 0 { ptr = (*C.uchar)(unsafe.Pointer(&content[0])) } - C.XChangeProperty(w.x, cevt.requestor, cevt.property, w.atoms.utf8string, + C.XChangeProperty(w.x, cevt.requestor, cevt.property, cevt.target, 8 /* bitwidth */, C.PropModeReplace, ptr, C.int(len(content)), ) @@ -458,7 +673,7 @@ func init() { x11Driver = newX11Window } -func newX11Window(gioWin Callbacks, opts *Options) error { +func newX11Window(gioWin *callbacks, options []Option) error { var err error pipe := make([]int, 2) @@ -498,6 +713,10 @@ func newX11Window(gioWin Callbacks, opts *Options) error { ppsp := x11DetectUIScale(dpy) cfg := unit.Metric{PxPerDp: ppsp, PxPerSp: ppsp} + // Only use cnf for getting the window size. + var cnf Config + cnf.apply(cfg, options) + swa := C.XSetWindowAttributes{ event_mask: C.ExposureMask | C.FocusChangeMask | // update C.KeyPressMask | C.KeyReleaseMask | // keyboard @@ -508,17 +727,17 @@ func newX11Window(gioWin Callbacks, opts *Options) error { override_redirect: C.False, } win := C.XCreateWindow(dpy, C.XDefaultRootWindow(dpy), - 0, 0, C.uint(cfg.Px(opts.Width)), C.uint(cfg.Px(opts.Height)), + 0, 0, C.uint(cnf.Size.X), C.uint(cnf.Size.Y), 0, C.CopyFromParent, C.InputOutput, nil, C.CWEventMask|C.CWBackPixmap|C.CWOverrideRedirect, &swa) w := &x11Window{ w: gioWin, x: dpy, xw: win, - width: cfg.Px(opts.Width), - height: cfg.Px(opts.Height), - cfg: cfg, + metric: cfg, xkb: xkb, xkbEventBase: xkbEventBase, + wakeups: make(chan struct{}, 1), + config: Config{Size: cnf.Size}, } w.notify.read = pipe[0] w.notify.write = pipe[1] @@ -533,57 +752,40 @@ func newX11Window(gioWin Callbacks, opts *Options) error { hints.flags = C.InputHint C.XSetWMHints(dpy, win, &hints) - var shints C.XSizeHints - if opts.MinWidth.V != 0 || opts.MinHeight.V != 0 { - shints.min_width = C.int(cfg.Px(opts.MinWidth)) - shints.min_height = C.int(cfg.Px(opts.MinHeight)) - shints.flags = C.PMinSize - } - if opts.MaxWidth.V != 0 || opts.MaxHeight.V != 0 { - shints.max_width = C.int(cfg.Px(opts.MaxWidth)) - shints.max_height = C.int(cfg.Px(opts.MaxHeight)) - shints.flags = shints.flags | C.PMaxSize - } - if shints.flags != 0 { - C.XSetWMNormalHints(dpy, win, &shints) - } - name := C.CString(filepath.Base(os.Args[0])) defer C.free(unsafe.Pointer(name)) wmhints := C.XClassHint{name, name} C.XSetClassHint(dpy, win, &wmhints) w.atoms.utf8string = w.atom("UTF8_STRING", false) + w.atoms.plaintext = w.atom("text/plain;charset=utf-8", false) + w.atoms.gtk_text_buffer_contents = w.atom("GTK_TEXT_BUFFER_CONTENTS", false) w.atoms.evDelWindow = w.atom("WM_DELETE_WINDOW", false) w.atoms.clipboard = w.atom("CLIPBOARD", false) + w.atoms.primary = w.atom("PRIMARY", false) w.atoms.clipboardContent = w.atom("CLIPBOARD_CONTENT", false) w.atoms.atom = w.atom("ATOM", false) w.atoms.targets = w.atom("TARGETS", false) - - // set the name - ctitle := C.CString(opts.Title) - defer C.free(unsafe.Pointer(ctitle)) - C.XStoreName(dpy, win, ctitle) - // set _NET_WM_NAME as well for UTF-8 support in window title. - C.XSetTextProperty(dpy, win, - &C.XTextProperty{ - value: (*C.uchar)(unsafe.Pointer(ctitle)), - encoding: w.atoms.utf8string, - format: 8, - nitems: C.ulong(len(opts.Title)), - }, - w.atom("_NET_WM_NAME", false)) + w.atoms.wmName = w.atom("_NET_WM_NAME", false) + w.atoms.wmState = w.atom("_NET_WM_STATE", false) + w.atoms.wmStateFullscreen = w.atom("_NET_WM_STATE_FULLSCREEN", false) + w.atoms.wmActiveWindow = w.atom("_NET_ACTIVE_WINDOW", false) + w.atoms.wmStateMaximizedHorz = w.atom("_NET_WM_STATE_MAXIMIZED_HORZ", false) + w.atoms.wmStateMaximizedVert = w.atom("_NET_WM_STATE_MAXIMIZED_VERT", false) // extensions C.XSetWMProtocols(dpy, win, &w.atoms.evDelWindow, 1) - // make the window visible on the screen - C.XMapWindow(dpy, win) - go func() { w.w.SetDriver(w) + w.Configure(options) + + // make the window visible on the screen + C.XMapWindow(dpy, win) + w.w.Event(ViewEvent{Display: unsafe.Pointer(dpy), Window: uintptr(win)}) w.setStage(system.StageRunning) w.loop() + w.w.Event(ViewEvent{}) w.destroy() }() return nil diff --git a/vendor/gioui.org/app/internal/window/runmain.go b/vendor/gioui.org/app/runmain.go index f9de5d0..a1c1e3d 100644 --- a/vendor/gioui.org/app/internal/window/runmain.go +++ b/vendor/gioui.org/app/runmain.go @@ -1,8 +1,9 @@ // SPDX-License-Identifier: Unlicense OR MIT +//go:build android || (darwin && ios) // +build android darwin,ios -package window +package app // Android only supports non-Java programs as c-shared libraries. // Unfortunately, Go does not run a program's main function in diff --git a/vendor/gioui.org/app/sigpipe_darwin.go b/vendor/gioui.org/app/sigpipe_darwin.go deleted file mode 100644 index aca19b7..0000000 --- a/vendor/gioui.org/app/sigpipe_darwin.go +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -// +build !go1.14 - -// Work around golang.org/issue/33384, fixed in CL 191785, -// to be released in Go 1.14. - -package app - -import ( - "os" - "os/signal" - "syscall" -) - -func init() { - signal.Notify(make(chan os.Signal), syscall.SIGPIPE) -} diff --git a/vendor/gioui.org/app/vulkan.go b/vendor/gioui.org/app/vulkan.go new file mode 100644 index 0000000..630c254 --- /dev/null +++ b/vendor/gioui.org/app/vulkan.go @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build (linux || freebsd) && !novulkan +// +build linux freebsd +// +build !novulkan + +package app + +import ( + "errors" + "unsafe" + + "gioui.org/gpu" + "gioui.org/internal/vk" +) + +type vkContext struct { + physDev vk.PhysicalDevice + inst vk.Instance + dev vk.Device + queueFam int + queue vk.Queue + acquireSem vk.Semaphore + presentSem vk.Semaphore + + swchain vk.Swapchain + imgs []vk.Image + views []vk.ImageView + fbos []vk.Framebuffer + format vk.Format + presentIdx int +} + +func newVulkanContext(inst vk.Instance, surf vk.Surface) (*vkContext, error) { + physDev, qFam, err := vk.ChoosePhysicalDevice(inst, surf) + if err != nil { + return nil, err + } + dev, err := vk.CreateDeviceAndQueue(physDev, qFam, "VK_KHR_swapchain") + if err != nil { + return nil, err + } + if err != nil { + vk.DestroyDevice(dev) + return nil, err + } + acquireSem, err := vk.CreateSemaphore(dev) + if err != nil { + vk.DestroyDevice(dev) + return nil, err + } + presentSem, err := vk.CreateSemaphore(dev) + if err != nil { + vk.DestroySemaphore(dev, acquireSem) + vk.DestroyDevice(dev) + return nil, err + } + c := &vkContext{ + physDev: physDev, + inst: inst, + dev: dev, + queueFam: qFam, + queue: vk.GetDeviceQueue(dev, qFam, 0), + acquireSem: acquireSem, + presentSem: presentSem, + } + return c, nil +} + +func (c *vkContext) RenderTarget() (gpu.RenderTarget, error) { + vk.DeviceWaitIdle(c.dev) + + imgIdx, err := vk.AcquireNextImage(c.dev, c.swchain, c.acquireSem, 0) + if err := mapSurfaceErr(err); err != nil { + return nil, err + } + c.presentIdx = imgIdx + return gpu.VulkanRenderTarget{ + WaitSem: uint64(c.acquireSem), + SignalSem: uint64(c.presentSem), + Framebuffer: uint64(c.fbos[imgIdx]), + Image: uint64(c.imgs[imgIdx]), + }, nil +} + +func (c *vkContext) api() gpu.API { + return gpu.Vulkan{ + PhysDevice: unsafe.Pointer(c.physDev), + Device: unsafe.Pointer(c.dev), + Format: int(c.format), + QueueFamily: c.queueFam, + QueueIndex: 0, + } +} + +func mapErr(err error) error { + var vkErr vk.Error + if errors.As(err, &vkErr) && vkErr == vk.ERROR_DEVICE_LOST { + return gpu.ErrDeviceLost + } + return err +} + +func mapSurfaceErr(err error) error { + var vkErr vk.Error + if !errors.As(err, &vkErr) { + return err + } + switch { + case vkErr == vk.SUBOPTIMAL_KHR: + // Android reports VK_SUBOPTIMAL_KHR when presenting to a rotated + // swapchain (preTransform != currentTransform). However, we don't + // support transforming the output ourselves, so we'll live with it. + return nil + case vkErr == vk.ERROR_OUT_OF_DATE_KHR: + return errOutOfDate + case vkErr == vk.ERROR_SURFACE_LOST_KHR: + // Treating a lost surface as a lost device isn't accurate, but + // probably not worth optimizing. + return gpu.ErrDeviceLost + } + return mapErr(err) +} + +func (c *vkContext) release() { + vk.DeviceWaitIdle(c.dev) + + c.destroySwapchain() + vk.DestroySemaphore(c.dev, c.acquireSem) + vk.DestroySemaphore(c.dev, c.presentSem) + vk.DestroyDevice(c.dev) + *c = vkContext{} +} + +func (c *vkContext) present() error { + return mapSurfaceErr(vk.PresentQueue(c.queue, c.swchain, c.presentSem, c.presentIdx)) +} + +func (c *vkContext) destroyImageViews() { + for _, f := range c.fbos { + vk.DestroyFramebuffer(c.dev, f) + } + c.fbos = nil + for _, view := range c.views { + vk.DestroyImageView(c.dev, view) + } + c.views = nil +} + +func (c *vkContext) destroySwapchain() { + vk.DeviceWaitIdle(c.dev) + + c.destroyImageViews() + if c.swchain != 0 { + vk.DestroySwapchain(c.dev, c.swchain) + c.swchain = 0 + } +} + +func (c *vkContext) refresh(surf vk.Surface, width, height int) error { + vk.DeviceWaitIdle(c.dev) + + c.destroyImageViews() + // Check whether size is valid. That's needed on X11, where ConfigureNotify + // is not always synchronized with the window extent. + caps, err := vk.GetPhysicalDeviceSurfaceCapabilities(c.physDev, surf) + if err != nil { + return err + } + minExt, maxExt := caps.MinExtent(), caps.MaxExtent() + if width < minExt.X || maxExt.X < width || height < minExt.Y || maxExt.Y < height { + return errOutOfDate + } + swchain, imgs, format, err := vk.CreateSwapchain(c.physDev, c.dev, surf, width, height, c.swchain) + if c.swchain != 0 { + vk.DestroySwapchain(c.dev, c.swchain) + c.swchain = 0 + } + if err := mapSurfaceErr(err); err != nil { + return err + } + c.swchain = swchain + c.imgs = imgs + c.format = format + pass, err := vk.CreateRenderPass( + c.dev, + format, + vk.ATTACHMENT_LOAD_OP_CLEAR, + vk.IMAGE_LAYOUT_UNDEFINED, + vk.IMAGE_LAYOUT_PRESENT_SRC_KHR, + nil, + ) + if err := mapErr(err); err != nil { + return err + } + defer vk.DestroyRenderPass(c.dev, pass) + for _, img := range imgs { + view, err := vk.CreateImageView(c.dev, img, format) + if err := mapErr(err); err != nil { + return err + } + c.views = append(c.views, view) + fbo, err := vk.CreateFramebuffer(c.dev, pass, view, width, height) + if err := mapErr(err); err != nil { + return err + } + c.fbos = append(c.fbos, fbo) + } + return nil +} diff --git a/vendor/gioui.org/app/vulkan_android.go b/vendor/gioui.org/app/vulkan_android.go new file mode 100644 index 0000000..3758d04 --- /dev/null +++ b/vendor/gioui.org/app/vulkan_android.go @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build !novulkan +// +build !novulkan + +package app + +import ( + "unsafe" + + "gioui.org/gpu" + "gioui.org/internal/vk" +) + +type wlVkContext struct { + win *window + inst vk.Instance + surf vk.Surface + ctx *vkContext +} + +func init() { + newAndroidVulkanContext = func(w *window) (context, error) { + inst, err := vk.CreateInstance("VK_KHR_surface", "VK_KHR_android_surface") + if err != nil { + return nil, err + } + window, _, _ := w.nativeWindow() + surf, err := vk.CreateAndroidSurface(inst, unsafe.Pointer(window)) + if err != nil { + vk.DestroyInstance(inst) + return nil, err + } + ctx, err := newVulkanContext(inst, surf) + if err != nil { + vk.DestroySurface(inst, surf) + vk.DestroyInstance(inst) + return nil, err + } + c := &wlVkContext{ + win: w, + inst: inst, + surf: surf, + ctx: ctx, + } + return c, nil + } +} + +func (c *wlVkContext) RenderTarget() (gpu.RenderTarget, error) { + return c.ctx.RenderTarget() +} + +func (c *wlVkContext) API() gpu.API { + return c.ctx.api() +} + +func (c *wlVkContext) Release() { + c.ctx.release() + if c.surf != 0 { + vk.DestroySurface(c.inst, c.surf) + } + vk.DestroyInstance(c.inst) + *c = wlVkContext{} +} + +func (c *wlVkContext) Present() error { + return c.ctx.present() +} + +func (c *wlVkContext) Lock() error { + return nil +} + +func (c *wlVkContext) Unlock() {} + +func (c *wlVkContext) Refresh() error { + win, w, h := c.win.nativeWindow() + if c.surf != 0 { + c.ctx.destroySwapchain() + vk.DestroySurface(c.inst, c.surf) + c.surf = 0 + } + surf, err := vk.CreateAndroidSurface(c.inst, unsafe.Pointer(win)) + if err != nil { + return err + } + c.surf = surf + return c.ctx.refresh(c.surf, w, h) +} diff --git a/vendor/gioui.org/app/vulkan_wayland.go b/vendor/gioui.org/app/vulkan_wayland.go new file mode 100644 index 0000000..7bf0662 --- /dev/null +++ b/vendor/gioui.org/app/vulkan_wayland.go @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build ((linux && !android) || freebsd) && !nowayland && !novulkan +// +build linux,!android freebsd +// +build !nowayland +// +build !novulkan + +package app + +import ( + "unsafe" + + "gioui.org/gpu" + "gioui.org/internal/vk" +) + +type wlVkContext struct { + win *window + inst vk.Instance + surf vk.Surface + ctx *vkContext +} + +func init() { + newWaylandVulkanContext = func(w *window) (context, error) { + inst, err := vk.CreateInstance("VK_KHR_surface", "VK_KHR_wayland_surface") + if err != nil { + return nil, err + } + disp := w.display() + wlSurf, _, _ := w.surface() + surf, err := vk.CreateWaylandSurface(inst, unsafe.Pointer(disp), unsafe.Pointer(wlSurf)) + if err != nil { + vk.DestroyInstance(inst) + return nil, err + } + ctx, err := newVulkanContext(inst, surf) + if err != nil { + vk.DestroySurface(inst, surf) + vk.DestroyInstance(inst) + return nil, err + } + c := &wlVkContext{ + win: w, + inst: inst, + surf: surf, + ctx: ctx, + } + return c, nil + } +} + +func (c *wlVkContext) RenderTarget() (gpu.RenderTarget, error) { + return c.ctx.RenderTarget() +} + +func (c *wlVkContext) API() gpu.API { + return c.ctx.api() +} + +func (c *wlVkContext) Release() { + c.ctx.release() + vk.DestroySurface(c.inst, c.surf) + vk.DestroyInstance(c.inst) + *c = wlVkContext{} +} + +func (c *wlVkContext) Present() error { + return c.ctx.present() +} + +func (c *wlVkContext) Lock() error { + return nil +} + +func (c *wlVkContext) Unlock() {} + +func (c *wlVkContext) Refresh() error { + _, w, h := c.win.surface() + return c.ctx.refresh(c.surf, w, h) +} diff --git a/vendor/gioui.org/app/vulkan_x11.go b/vendor/gioui.org/app/vulkan_x11.go new file mode 100644 index 0000000..cf97774 --- /dev/null +++ b/vendor/gioui.org/app/vulkan_x11.go @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build ((linux && !android) || freebsd) && !nox11 && !novulkan +// +build linux,!android freebsd +// +build !nox11 +// +build !novulkan + +package app + +import ( + "unsafe" + + "gioui.org/gpu" + "gioui.org/internal/vk" +) + +type x11VkContext struct { + win *x11Window + inst vk.Instance + surf vk.Surface + ctx *vkContext +} + +func init() { + newX11VulkanContext = func(w *x11Window) (context, error) { + inst, err := vk.CreateInstance("VK_KHR_surface", "VK_KHR_xlib_surface") + if err != nil { + return nil, err + } + disp := w.display() + window, _, _ := w.window() + surf, err := vk.CreateXlibSurface(inst, unsafe.Pointer(disp), uintptr(window)) + if err != nil { + vk.DestroyInstance(inst) + return nil, err + } + ctx, err := newVulkanContext(inst, surf) + if err != nil { + vk.DestroySurface(inst, surf) + vk.DestroyInstance(inst) + return nil, err + } + c := &x11VkContext{ + win: w, + inst: inst, + surf: surf, + ctx: ctx, + } + return c, nil + } +} + +func (c *x11VkContext) RenderTarget() (gpu.RenderTarget, error) { + return c.ctx.RenderTarget() +} + +func (c *x11VkContext) API() gpu.API { + return c.ctx.api() +} + +func (c *x11VkContext) Release() { + c.ctx.release() + vk.DestroySurface(c.inst, c.surf) + vk.DestroyInstance(c.inst) + *c = x11VkContext{} +} + +func (c *x11VkContext) Present() error { + return c.ctx.present() +} + +func (c *x11VkContext) Lock() error { + return nil +} + +func (c *x11VkContext) Unlock() {} + +func (c *x11VkContext) Refresh() error { + _, w, h := c.win.window() + return c.ctx.refresh(c.surf, w, h) +} diff --git a/vendor/gioui.org/app/internal/window/wayland_text_input.c b/vendor/gioui.org/app/wayland_text_input.c index a69af09..65de0bb 100644 --- a/vendor/gioui.org/app/internal/window/wayland_text_input.c +++ b/vendor/gioui.org/app/wayland_text_input.c @@ -1,6 +1,8 @@ -// +build linux,!android,!nowayland freebsd +//go:build ((linux && !android) || freebsd) && !nowayland +// +build linux,!android freebsd +// +build !nowayland -/* Generated by wayland-scanner 1.17.0 */ +/* Generated by wayland-scanner 1.19.0 */ /* * Copyright © 2012, 2013 Intel Corporation @@ -48,7 +50,7 @@ extern const struct wl_interface wl_seat_interface; extern const struct wl_interface wl_surface_interface; extern const struct wl_interface zwp_text_input_v3_interface; -static const struct wl_interface *types[] = { +static const struct wl_interface *text_input_unstable_v3_types[] = { NULL, NULL, NULL, @@ -60,23 +62,23 @@ static const struct wl_interface *types[] = { }; static const struct wl_message zwp_text_input_v3_requests[] = { - { "destroy", "", types + 0 }, - { "enable", "", types + 0 }, - { "disable", "", types + 0 }, - { "set_surrounding_text", "sii", types + 0 }, - { "set_text_change_cause", "u", types + 0 }, - { "set_content_type", "uu", types + 0 }, - { "set_cursor_rectangle", "iiii", types + 0 }, - { "commit", "", types + 0 }, + { "destroy", "", text_input_unstable_v3_types + 0 }, + { "enable", "", text_input_unstable_v3_types + 0 }, + { "disable", "", text_input_unstable_v3_types + 0 }, + { "set_surrounding_text", "sii", text_input_unstable_v3_types + 0 }, + { "set_text_change_cause", "u", text_input_unstable_v3_types + 0 }, + { "set_content_type", "uu", text_input_unstable_v3_types + 0 }, + { "set_cursor_rectangle", "iiii", text_input_unstable_v3_types + 0 }, + { "commit", "", text_input_unstable_v3_types + 0 }, }; static const struct wl_message zwp_text_input_v3_events[] = { - { "enter", "o", types + 4 }, - { "leave", "o", types + 5 }, - { "preedit_string", "?sii", types + 0 }, - { "commit_string", "?s", types + 0 }, - { "delete_surrounding_text", "uu", types + 0 }, - { "done", "u", types + 0 }, + { "enter", "o", text_input_unstable_v3_types + 4 }, + { "leave", "o", text_input_unstable_v3_types + 5 }, + { "preedit_string", "?sii", text_input_unstable_v3_types + 0 }, + { "commit_string", "?s", text_input_unstable_v3_types + 0 }, + { "delete_surrounding_text", "uu", text_input_unstable_v3_types + 0 }, + { "done", "u", text_input_unstable_v3_types + 0 }, }; WL_PRIVATE const struct wl_interface zwp_text_input_v3_interface = { @@ -86,8 +88,8 @@ WL_PRIVATE const struct wl_interface zwp_text_input_v3_interface = { }; static const struct wl_message zwp_text_input_manager_v3_requests[] = { - { "destroy", "", types + 0 }, - { "get_text_input", "no", types + 6 }, + { "destroy", "", text_input_unstable_v3_types + 0 }, + { "get_text_input", "no", text_input_unstable_v3_types + 6 }, }; WL_PRIVATE const struct wl_interface zwp_text_input_manager_v3_interface = { diff --git a/vendor/gioui.org/app/internal/window/wayland_text_input.h b/vendor/gioui.org/app/wayland_text_input.h index 8a6f8dd..882da43 100644 --- a/vendor/gioui.org/app/internal/window/wayland_text_input.h +++ b/vendor/gioui.org/app/wayland_text_input.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.17.0 */ +/* Generated by wayland-scanner 1.19.0 */ #ifndef TEXT_INPUT_UNSTABLE_V3_CLIENT_PROTOCOL_H #define TEXT_INPUT_UNSTABLE_V3_CLIENT_PROTOCOL_H @@ -71,6 +71,8 @@ struct wl_surface; struct zwp_text_input_manager_v3; struct zwp_text_input_v3; +#ifndef ZWP_TEXT_INPUT_V3_INTERFACE +#define ZWP_TEXT_INPUT_V3_INTERFACE /** * @page page_iface_zwp_text_input_v3 zwp_text_input_v3 * @section page_iface_zwp_text_input_v3_desc Description @@ -135,6 +137,9 @@ struct zwp_text_input_v3; * needs to be resent by the client. */ extern const struct wl_interface zwp_text_input_v3_interface; +#endif +#ifndef ZWP_TEXT_INPUT_MANAGER_V3_INTERFACE +#define ZWP_TEXT_INPUT_MANAGER_V3_INTERFACE /** * @page page_iface_zwp_text_input_manager_v3 zwp_text_input_manager_v3 * @section page_iface_zwp_text_input_manager_v3_desc Description @@ -149,6 +154,7 @@ extern const struct wl_interface zwp_text_input_v3_interface; * A factory for text-input objects. This object is a global singleton. */ extern const struct wl_interface zwp_text_input_manager_v3_interface; +#endif #ifndef ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_ENUM #define ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_ENUM @@ -310,6 +316,9 @@ struct zwp_text_input_v3_listener { * Notification that this seat's text-input focus is on a certain * surface. * + * If client has created multiple text input objects, compositor + * must send this event to all of them. + * * When the seat has the keyboard capability the text-input focus * follows the keyboard focus. This event sets the current surface * for the text-input object. @@ -325,7 +334,9 @@ struct zwp_text_input_v3_listener { * previously set. * * The leave notification clears the current surface. It is sent - * before the enter notification for the new focus. + * before the enter notification for the new focus. After leave + * event, compositor must ignore requests from any text input + * instances until next enter event. * * When the seat has the keyboard capability the text-input focus * follows the keyboard focus. @@ -559,6 +570,12 @@ zwp_text_input_v3_destroy(struct zwp_text_input_v3 *zwp_text_input_v3) * zwp_text_input_v3.disable when there is no longer any input focus on * the current surface. * + * Clients must not enable more than one text input on the single seat + * and should disable the current text input before enabling the new one. + * At most one instance of text input may be in enabled state per instance, + * Requests to enable the another text input when some text input is active + * must be ignored by compositor. + * * This request resets all state associated with previous enable, disable, * set_surrounding_text, set_text_change_cause, set_content_type, and * set_cursor_rectangle requests, as well as the state associated with diff --git a/vendor/gioui.org/app/internal/window/wayland_xdg_decoration.c b/vendor/gioui.org/app/wayland_xdg_decoration.c index fa13d7c..ee94c60 100644 --- a/vendor/gioui.org/app/internal/window/wayland_xdg_decoration.c +++ b/vendor/gioui.org/app/wayland_xdg_decoration.c @@ -1,6 +1,8 @@ -// +build linux,!android,!nowayland freebsd +//go:build ((linux && !android) || freebsd) && !nowayland +// +build linux,!android freebsd +// +build !nowayland -/* Generated by wayland-scanner 1.17.0 */ +/* Generated by wayland-scanner 1.19.0 */ /* * Copyright © 2018 Simon Ser @@ -42,15 +44,15 @@ extern const struct wl_interface xdg_toplevel_interface; extern const struct wl_interface zxdg_toplevel_decoration_v1_interface; -static const struct wl_interface *types[] = { +static const struct wl_interface *xdg_decoration_unstable_v1_types[] = { NULL, &zxdg_toplevel_decoration_v1_interface, &xdg_toplevel_interface, }; static const struct wl_message zxdg_decoration_manager_v1_requests[] = { - { "destroy", "", types + 0 }, - { "get_toplevel_decoration", "no", types + 1 }, + { "destroy", "", xdg_decoration_unstable_v1_types + 0 }, + { "get_toplevel_decoration", "no", xdg_decoration_unstable_v1_types + 1 }, }; WL_PRIVATE const struct wl_interface zxdg_decoration_manager_v1_interface = { @@ -60,13 +62,13 @@ WL_PRIVATE const struct wl_interface zxdg_decoration_manager_v1_interface = { }; static const struct wl_message zxdg_toplevel_decoration_v1_requests[] = { - { "destroy", "", types + 0 }, - { "set_mode", "u", types + 0 }, - { "unset_mode", "", types + 0 }, + { "destroy", "", xdg_decoration_unstable_v1_types + 0 }, + { "set_mode", "u", xdg_decoration_unstable_v1_types + 0 }, + { "unset_mode", "", xdg_decoration_unstable_v1_types + 0 }, }; static const struct wl_message zxdg_toplevel_decoration_v1_events[] = { - { "configure", "u", types + 0 }, + { "configure", "u", xdg_decoration_unstable_v1_types + 0 }, }; WL_PRIVATE const struct wl_interface zxdg_toplevel_decoration_v1_interface = { diff --git a/vendor/gioui.org/app/internal/window/wayland_xdg_decoration.h b/vendor/gioui.org/app/wayland_xdg_decoration.h index 044ea2e..004d342 100644 --- a/vendor/gioui.org/app/internal/window/wayland_xdg_decoration.h +++ b/vendor/gioui.org/app/wayland_xdg_decoration.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.17.0 */ +/* Generated by wayland-scanner 1.19.0 */ #ifndef XDG_DECORATION_UNSTABLE_V1_CLIENT_PROTOCOL_H #define XDG_DECORATION_UNSTABLE_V1_CLIENT_PROTOCOL_H @@ -45,6 +45,8 @@ struct xdg_toplevel; struct zxdg_decoration_manager_v1; struct zxdg_toplevel_decoration_v1; +#ifndef ZXDG_DECORATION_MANAGER_V1_INTERFACE +#define ZXDG_DECORATION_MANAGER_V1_INTERFACE /** * @page page_iface_zxdg_decoration_manager_v1 zxdg_decoration_manager_v1 * @section page_iface_zxdg_decoration_manager_v1_desc Description @@ -101,6 +103,9 @@ struct zxdg_toplevel_decoration_v1; * interface version number is reset. */ extern const struct wl_interface zxdg_decoration_manager_v1_interface; +#endif +#ifndef ZXDG_TOPLEVEL_DECORATION_V1_INTERFACE +#define ZXDG_TOPLEVEL_DECORATION_V1_INTERFACE /** * @page page_iface_zxdg_toplevel_decoration_v1 zxdg_toplevel_decoration_v1 * @section page_iface_zxdg_toplevel_decoration_v1_desc Description @@ -125,6 +130,7 @@ extern const struct wl_interface zxdg_decoration_manager_v1_interface; * xdg_toplevel. */ extern const struct wl_interface zxdg_toplevel_decoration_v1_interface; +#endif #define ZXDG_DECORATION_MANAGER_V1_DESTROY 0 #define ZXDG_DECORATION_MANAGER_V1_GET_TOPLEVEL_DECORATION 1 @@ -332,7 +338,7 @@ zxdg_toplevel_decoration_v1_destroy(struct zxdg_toplevel_decoration_v1 *zxdg_top * that the client prefers the provided decoration mode. * * After requesting a decoration mode, the compositor will respond by - * emitting a xdg_surface.configure event. The client should then update + * emitting an xdg_surface.configure event. The client should then update * its content, drawing it without decorations if the received mode is * server-side decorations. The client must also acknowledge the configure * when committing the new content (see xdg_surface.ack_configure). @@ -341,7 +347,7 @@ zxdg_toplevel_decoration_v1_destroy(struct zxdg_toplevel_decoration_v1 *zxdg_top * different mode instead. * * Clients whose decoration mode depend on the xdg_toplevel state may send - * a set_mode request in response to a xdg_surface.configure event and wait + * a set_mode request in response to an xdg_surface.configure event and wait * for the next xdg_surface.configure event to prevent unwanted state. * Such clients are responsible for preventing configure loops and must * make sure not to send multiple successive set_mode requests with the diff --git a/vendor/gioui.org/app/internal/window/wayland_xdg_shell.c b/vendor/gioui.org/app/wayland_xdg_shell.c index 1947847..54fe5cf 100644 --- a/vendor/gioui.org/app/internal/window/wayland_xdg_shell.c +++ b/vendor/gioui.org/app/wayland_xdg_shell.c @@ -1,6 +1,8 @@ -// +build linux,!android,!nowayland freebsd +//go:build ((linux && !android) || freebsd) && !nowayland +// +build linux,!android freebsd +// +build !nowayland -/* Generated by wayland-scanner 1.17.0 */ +/* Generated by wayland-scanner 1.19.0 */ /* * Copyright © 2008-2013 Kristian Høgsberg @@ -52,7 +54,7 @@ extern const struct wl_interface xdg_positioner_interface; extern const struct wl_interface xdg_surface_interface; extern const struct wl_interface xdg_toplevel_interface; -static const struct wl_interface *types[] = { +static const struct wl_interface *xdg_shell_types[] = { NULL, NULL, NULL, @@ -77,100 +79,107 @@ static const struct wl_interface *types[] = { &wl_output_interface, &wl_seat_interface, NULL, + &xdg_positioner_interface, + NULL, }; static const struct wl_message xdg_wm_base_requests[] = { - { "destroy", "", types + 0 }, - { "create_positioner", "n", types + 4 }, - { "get_xdg_surface", "no", types + 5 }, - { "pong", "u", types + 0 }, + { "destroy", "", xdg_shell_types + 0 }, + { "create_positioner", "n", xdg_shell_types + 4 }, + { "get_xdg_surface", "no", xdg_shell_types + 5 }, + { "pong", "u", xdg_shell_types + 0 }, }; static const struct wl_message xdg_wm_base_events[] = { - { "ping", "u", types + 0 }, + { "ping", "u", xdg_shell_types + 0 }, }; WL_PRIVATE const struct wl_interface xdg_wm_base_interface = { - "xdg_wm_base", 2, + "xdg_wm_base", 3, 4, xdg_wm_base_requests, 1, xdg_wm_base_events, }; static const struct wl_message xdg_positioner_requests[] = { - { "destroy", "", types + 0 }, - { "set_size", "ii", types + 0 }, - { "set_anchor_rect", "iiii", types + 0 }, - { "set_anchor", "u", types + 0 }, - { "set_gravity", "u", types + 0 }, - { "set_constraint_adjustment", "u", types + 0 }, - { "set_offset", "ii", types + 0 }, + { "destroy", "", xdg_shell_types + 0 }, + { "set_size", "ii", xdg_shell_types + 0 }, + { "set_anchor_rect", "iiii", xdg_shell_types + 0 }, + { "set_anchor", "u", xdg_shell_types + 0 }, + { "set_gravity", "u", xdg_shell_types + 0 }, + { "set_constraint_adjustment", "u", xdg_shell_types + 0 }, + { "set_offset", "ii", xdg_shell_types + 0 }, + { "set_reactive", "3", xdg_shell_types + 0 }, + { "set_parent_size", "3ii", xdg_shell_types + 0 }, + { "set_parent_configure", "3u", xdg_shell_types + 0 }, }; WL_PRIVATE const struct wl_interface xdg_positioner_interface = { - "xdg_positioner", 2, - 7, xdg_positioner_requests, + "xdg_positioner", 3, + 10, xdg_positioner_requests, 0, NULL, }; static const struct wl_message xdg_surface_requests[] = { - { "destroy", "", types + 0 }, - { "get_toplevel", "n", types + 7 }, - { "get_popup", "n?oo", types + 8 }, - { "set_window_geometry", "iiii", types + 0 }, - { "ack_configure", "u", types + 0 }, + { "destroy", "", xdg_shell_types + 0 }, + { "get_toplevel", "n", xdg_shell_types + 7 }, + { "get_popup", "n?oo", xdg_shell_types + 8 }, + { "set_window_geometry", "iiii", xdg_shell_types + 0 }, + { "ack_configure", "u", xdg_shell_types + 0 }, }; static const struct wl_message xdg_surface_events[] = { - { "configure", "u", types + 0 }, + { "configure", "u", xdg_shell_types + 0 }, }; WL_PRIVATE const struct wl_interface xdg_surface_interface = { - "xdg_surface", 2, + "xdg_surface", 3, 5, xdg_surface_requests, 1, xdg_surface_events, }; static const struct wl_message xdg_toplevel_requests[] = { - { "destroy", "", types + 0 }, - { "set_parent", "?o", types + 11 }, - { "set_title", "s", types + 0 }, - { "set_app_id", "s", types + 0 }, - { "show_window_menu", "ouii", types + 12 }, - { "move", "ou", types + 16 }, - { "resize", "ouu", types + 18 }, - { "set_max_size", "ii", types + 0 }, - { "set_min_size", "ii", types + 0 }, - { "set_maximized", "", types + 0 }, - { "unset_maximized", "", types + 0 }, - { "set_fullscreen", "?o", types + 21 }, - { "unset_fullscreen", "", types + 0 }, - { "set_minimized", "", types + 0 }, + { "destroy", "", xdg_shell_types + 0 }, + { "set_parent", "?o", xdg_shell_types + 11 }, + { "set_title", "s", xdg_shell_types + 0 }, + { "set_app_id", "s", xdg_shell_types + 0 }, + { "show_window_menu", "ouii", xdg_shell_types + 12 }, + { "move", "ou", xdg_shell_types + 16 }, + { "resize", "ouu", xdg_shell_types + 18 }, + { "set_max_size", "ii", xdg_shell_types + 0 }, + { "set_min_size", "ii", xdg_shell_types + 0 }, + { "set_maximized", "", xdg_shell_types + 0 }, + { "unset_maximized", "", xdg_shell_types + 0 }, + { "set_fullscreen", "?o", xdg_shell_types + 21 }, + { "unset_fullscreen", "", xdg_shell_types + 0 }, + { "set_minimized", "", xdg_shell_types + 0 }, }; static const struct wl_message xdg_toplevel_events[] = { - { "configure", "iia", types + 0 }, - { "close", "", types + 0 }, + { "configure", "iia", xdg_shell_types + 0 }, + { "close", "", xdg_shell_types + 0 }, }; WL_PRIVATE const struct wl_interface xdg_toplevel_interface = { - "xdg_toplevel", 2, + "xdg_toplevel", 3, 14, xdg_toplevel_requests, 2, xdg_toplevel_events, }; static const struct wl_message xdg_popup_requests[] = { - { "destroy", "", types + 0 }, - { "grab", "ou", types + 22 }, + { "destroy", "", xdg_shell_types + 0 }, + { "grab", "ou", xdg_shell_types + 22 }, + { "reposition", "3ou", xdg_shell_types + 24 }, }; static const struct wl_message xdg_popup_events[] = { - { "configure", "iiii", types + 0 }, - { "popup_done", "", types + 0 }, + { "configure", "iiii", xdg_shell_types + 0 }, + { "popup_done", "", xdg_shell_types + 0 }, + { "repositioned", "3u", xdg_shell_types + 0 }, }; WL_PRIVATE const struct wl_interface xdg_popup_interface = { - "xdg_popup", 2, - 2, xdg_popup_requests, - 2, xdg_popup_events, + "xdg_popup", 3, + 3, xdg_popup_requests, + 3, xdg_popup_events, }; diff --git a/vendor/gioui.org/app/internal/window/wayland_xdg_shell.h b/vendor/gioui.org/app/wayland_xdg_shell.h index 1f4bfb5..1db8fd9 100644 --- a/vendor/gioui.org/app/internal/window/wayland_xdg_shell.h +++ b/vendor/gioui.org/app/wayland_xdg_shell.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.17.0 */ +/* Generated by wayland-scanner 1.19.0 */ #ifndef XDG_SHELL_CLIENT_PROTOCOL_H #define XDG_SHELL_CLIENT_PROTOCOL_H @@ -58,6 +58,8 @@ struct xdg_surface; struct xdg_toplevel; struct xdg_wm_base; +#ifndef XDG_WM_BASE_INTERFACE +#define XDG_WM_BASE_INTERFACE /** * @page page_iface_xdg_wm_base xdg_wm_base * @section page_iface_xdg_wm_base_desc Description @@ -80,6 +82,9 @@ struct xdg_wm_base; * creating transient windows such as popup menus. */ extern const struct wl_interface xdg_wm_base_interface; +#endif +#ifndef XDG_POSITIONER_INTERFACE +#define XDG_POSITIONER_INTERFACE /** * @page page_iface_xdg_positioner xdg_positioner * @section page_iface_xdg_positioner_desc Description @@ -130,6 +135,9 @@ extern const struct wl_interface xdg_wm_base_interface; * positioning a surface raises an error. */ extern const struct wl_interface xdg_positioner_interface; +#endif +#ifndef XDG_SURFACE_INTERFACE +#define XDG_SURFACE_INTERFACE /** * @page page_iface_xdg_surface xdg_surface * @section page_iface_xdg_surface_desc Description @@ -159,6 +167,11 @@ extern const struct wl_interface xdg_positioner_interface; * manipulate a buffer prior to the first xdg_surface.configure call must * also be treated as errors. * + * After creating a role-specific object and setting it up, the client must + * perform an initial commit without any buffer attached. The compositor + * will reply with an xdg_surface.configure event. The client must + * acknowledge it and is then allowed to attach a buffer to map the surface. + * * Mapping an xdg_surface-based role surface is defined as making it * possible for the surface to be shown by the compositor. Note that * a mapped surface is not guaranteed to be visible once it is mapped. @@ -204,6 +217,11 @@ extern const struct wl_interface xdg_positioner_interface; * manipulate a buffer prior to the first xdg_surface.configure call must * also be treated as errors. * + * After creating a role-specific object and setting it up, the client must + * perform an initial commit without any buffer attached. The compositor + * will reply with an xdg_surface.configure event. The client must + * acknowledge it and is then allowed to attach a buffer to map the surface. + * * Mapping an xdg_surface-based role surface is defined as making it * possible for the surface to be shown by the compositor. Note that * a mapped surface is not guaranteed to be visible once it is mapped. @@ -220,6 +238,9 @@ extern const struct wl_interface xdg_positioner_interface; * has not been destroyed. */ extern const struct wl_interface xdg_surface_interface; +#endif +#ifndef XDG_TOPLEVEL_INTERFACE +#define XDG_TOPLEVEL_INTERFACE /** * @page page_iface_xdg_toplevel xdg_toplevel * @section page_iface_xdg_toplevel_desc Description @@ -234,7 +255,11 @@ extern const struct wl_interface xdg_surface_interface; * by the compositor until it is explicitly mapped again. * All active operations (e.g., move, resize) are canceled and all * attributes (e.g. title, state, stacking, ...) are discarded for - * an xdg_toplevel surface when it is unmapped. + * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to + * the state it had right after xdg_surface.get_toplevel. The client + * can re-map the toplevel by perfoming a commit without any buffer + * attached, waiting for a configure event and handling it as usual (see + * xdg_surface description). * * Attaching a null buffer to a toplevel unmaps the surface. * @section page_iface_xdg_toplevel_api API @@ -253,11 +278,18 @@ extern const struct wl_interface xdg_surface_interface; * by the compositor until it is explicitly mapped again. * All active operations (e.g., move, resize) are canceled and all * attributes (e.g. title, state, stacking, ...) are discarded for - * an xdg_toplevel surface when it is unmapped. + * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to + * the state it had right after xdg_surface.get_toplevel. The client + * can re-map the toplevel by perfoming a commit without any buffer + * attached, waiting for a configure event and handling it as usual (see + * xdg_surface description). * * Attaching a null buffer to a toplevel unmaps the surface. */ extern const struct wl_interface xdg_toplevel_interface; +#endif +#ifndef XDG_POPUP_INTERFACE +#define XDG_POPUP_INTERFACE /** * @page page_iface_xdg_popup xdg_popup * @section page_iface_xdg_popup_desc Description @@ -284,12 +316,6 @@ extern const struct wl_interface xdg_toplevel_interface; * The parent of an xdg_popup must be mapped (see the xdg_surface * description) before the xdg_popup itself. * - * The x and y arguments passed when creating the popup object specify - * where the top left of the popup should be placed, relative to the - * local surface coordinates of the parent surface. See - * xdg_surface.get_popup. An xdg_popup must intersect with or be at least - * partially adjacent to its parent surface. - * * The client must call wl_surface.commit on the corresponding wl_surface * for the xdg_popup state to take effect. * @section page_iface_xdg_popup_api API @@ -320,16 +346,11 @@ extern const struct wl_interface xdg_toplevel_interface; * The parent of an xdg_popup must be mapped (see the xdg_surface * description) before the xdg_popup itself. * - * The x and y arguments passed when creating the popup object specify - * where the top left of the popup should be placed, relative to the - * local surface coordinates of the parent surface. See - * xdg_surface.get_popup. An xdg_popup must intersect with or be at least - * partially adjacent to its parent surface. - * * The client must call wl_surface.commit on the corresponding wl_surface * for the xdg_popup state to take effect. */ extern const struct wl_interface xdg_popup_interface; +#endif #ifndef XDG_WM_BASE_ERROR_ENUM #define XDG_WM_BASE_ERROR_ENUM @@ -587,6 +608,9 @@ enum xdg_positioner_constraint_adjustment { #define XDG_POSITIONER_SET_GRAVITY 4 #define XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT 5 #define XDG_POSITIONER_SET_OFFSET 6 +#define XDG_POSITIONER_SET_REACTIVE 7 +#define XDG_POSITIONER_SET_PARENT_SIZE 8 +#define XDG_POSITIONER_SET_PARENT_CONFIGURE 9 /** @@ -617,6 +641,18 @@ enum xdg_positioner_constraint_adjustment { * @ingroup iface_xdg_positioner */ #define XDG_POSITIONER_SET_OFFSET_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_REACTIVE_SINCE_VERSION 3 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_PARENT_SIZE_SINCE_VERSION 3 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_PARENT_CONFIGURE_SINCE_VERSION 3 /** @ingroup iface_xdg_positioner */ static inline void @@ -769,6 +805,56 @@ xdg_positioner_set_offset(struct xdg_positioner *xdg_positioner, int32_t x, int3 XDG_POSITIONER_SET_OFFSET, x, y); } +/** + * @ingroup iface_xdg_positioner + * + * When set reactive, the surface is reconstrained if the conditions used + * for constraining changed, e.g. the parent window moved. + * + * If the conditions changed and the popup was reconstrained, an + * xdg_popup.configure event is sent with updated geometry, followed by an + * xdg_surface.configure event. + */ +static inline void +xdg_positioner_set_reactive(struct xdg_positioner *xdg_positioner) +{ + wl_proxy_marshal((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_REACTIVE); +} + +/** + * @ingroup iface_xdg_positioner + * + * Set the parent window geometry the compositor should use when + * positioning the popup. The compositor may use this information to + * determine the future state the popup should be constrained using. If + * this doesn't match the dimension of the parent the popup is eventually + * positioned against, the behavior is undefined. + * + * The arguments are given in the surface-local coordinate space. + */ +static inline void +xdg_positioner_set_parent_size(struct xdg_positioner *xdg_positioner, int32_t parent_width, int32_t parent_height) +{ + wl_proxy_marshal((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_PARENT_SIZE, parent_width, parent_height); +} + +/** + * @ingroup iface_xdg_positioner + * + * Set the serial of an xdg_surface.configure event this positioner will be + * used in response to. The compositor may use this information together + * with set_parent_size to determine what future state the popup should be + * constrained using. + */ +static inline void +xdg_positioner_set_parent_configure(struct xdg_positioner *xdg_positioner, uint32_t serial) +{ + wl_proxy_marshal((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_PARENT_CONFIGURE, serial); +} + #ifndef XDG_SURFACE_ERROR_ENUM #define XDG_SURFACE_ERROR_ENUM enum xdg_surface_error { @@ -1691,6 +1777,12 @@ struct xdg_popup_listener { * The x and y arguments represent the position the popup was * placed at given the xdg_positioner rule, relative to the upper * left corner of the window geometry of the parent surface. + * + * For version 2 or older, the configure event for an xdg_popup is + * only ever sent once for the initial configuration. Starting with + * version 3, it may be sent again if the popup is setup with an + * xdg_positioner with set_reactive requested, or in response to + * xdg_popup.reposition requests. * @param x x position relative to parent surface window geometry * @param y y position relative to parent surface window geometry * @param width window geometry width @@ -1711,6 +1803,32 @@ struct xdg_popup_listener { */ void (*popup_done)(void *data, struct xdg_popup *xdg_popup); + /** + * signal the completion of a repositioned request + * + * The repositioned event is sent as part of a popup + * configuration sequence, together with xdg_popup.configure and + * lastly xdg_surface.configure to notify the completion of a + * reposition request. + * + * The repositioned event is to notify about the completion of a + * xdg_popup.reposition request. The token argument is the token + * passed in the xdg_popup.reposition request. + * + * Immediately after this event is emitted, xdg_popup.configure and + * xdg_surface.configure will be sent with the updated size and + * position, as well as a new configure serial. + * + * The client should optionally update the content of the popup, + * but must acknowledge the new popup configuration for the new + * position to take effect. See xdg_surface.ack_configure for + * details. + * @param token reposition request token + * @since 3 + */ + void (*repositioned)(void *data, + struct xdg_popup *xdg_popup, + uint32_t token); }; /** @@ -1726,6 +1844,7 @@ xdg_popup_add_listener(struct xdg_popup *xdg_popup, #define XDG_POPUP_DESTROY 0 #define XDG_POPUP_GRAB 1 +#define XDG_POPUP_REPOSITION 2 /** * @ingroup iface_xdg_popup @@ -1735,6 +1854,10 @@ xdg_popup_add_listener(struct xdg_popup *xdg_popup, * @ingroup iface_xdg_popup */ #define XDG_POPUP_POPUP_DONE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_REPOSITIONED_SINCE_VERSION 3 /** * @ingroup iface_xdg_popup @@ -1744,6 +1867,10 @@ xdg_popup_add_listener(struct xdg_popup *xdg_popup, * @ingroup iface_xdg_popup */ #define XDG_POPUP_GRAB_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_REPOSITION_SINCE_VERSION 3 /** @ingroup iface_xdg_popup */ static inline void @@ -1835,6 +1962,40 @@ xdg_popup_grab(struct xdg_popup *xdg_popup, struct wl_seat *seat, uint32_t seria XDG_POPUP_GRAB, seat, serial); } +/** + * @ingroup iface_xdg_popup + * + * Reposition an already-mapped popup. The popup will be placed given the + * details in the passed xdg_positioner object, and a + * xdg_popup.repositioned followed by xdg_popup.configure and + * xdg_surface.configure will be emitted in response. Any parameters set + * by the previous positioner will be discarded. + * + * The passed token will be sent in the corresponding + * xdg_popup.repositioned event. The new popup position will not take + * effect until the corresponding configure event is acknowledged by the + * client. See xdg_popup.repositioned for details. The token itself is + * opaque, and has no other special meaning. + * + * If multiple reposition requests are sent, the compositor may skip all + * but the last one. + * + * If the popup is repositioned in response to a configure event for its + * parent, the client should send an xdg_positioner.set_parent_configure + * and possibly an xdg_positioner.set_parent_size request to allow the + * compositor to properly constrain the popup. + * + * If the popup is repositioned together with a parent that is being + * resized, but not in response to a configure event, the client should + * send an xdg_positioner.set_parent_size request. + */ +static inline void +xdg_popup_reposition(struct xdg_popup *xdg_popup, struct xdg_positioner *positioner, uint32_t token) +{ + wl_proxy_marshal((struct wl_proxy *) xdg_popup, + XDG_POPUP_REPOSITION, positioner, token); +} + #ifdef __cplusplus } #endif diff --git a/vendor/gioui.org/app/window.go b/vendor/gioui.org/app/window.go index 2c9860c..ada92fb 100644 --- a/vendor/gioui.org/app/window.go +++ b/vendor/gioui.org/app/window.go @@ -6,10 +6,14 @@ import ( "errors" "fmt" "image" + "image/color" + "runtime" "time" - "gioui.org/app/internal/window" + "gioui.org/f32" + "gioui.org/gpu" "gioui.org/io/event" + "gioui.org/io/pointer" "gioui.org/io/profile" "gioui.org/io/router" "gioui.org/io/system" @@ -19,24 +23,32 @@ import ( _ "gioui.org/app/internal/log" ) -// WindowOption configures a Window. -type Option func(opts *window.Options) +// Option configures a window. +type Option func(unit.Metric, *Config) // Window represents an operating system window. type Window struct { - driver window.Driver - loop *renderLoop + ctx context + gpu gpu.GPU // driverFuncs is a channel of functions to run when // the Window has a valid driver. - driverFuncs chan func() - - out chan event.Event - in chan event.Event - ack chan struct{} - invalidates chan struct{} - frames chan *op.Ops - frameAck chan struct{} + driverFuncs chan func(d driver) + // wakeups wakes up the native event loop to send a + // WakeupEvent that flushes driverFuncs. + wakeups chan struct{} + // wakeupFuncs is sent wakeup functions when the driver changes. + wakeupFuncs chan func() + // redraws is notified when a redraw is requested by the client. + redraws chan struct{} + // immediateRedraws is like redraw but doesn't need a wakeup. + immediateRedraws chan struct{} + // scheduledRedraws is sent the most recent delayed redraw time. + scheduledRedraws chan time.Time + + out chan event.Event + frames chan *op.Ops + frameAck chan struct{} // dead is closed when the window is destroyed. dead chan struct{} @@ -46,13 +58,33 @@ type Window struct { nextFrame time.Time delayedDraw *time.Timer - queue queue + queue queue + cursor pointer.CursorName callbacks callbacks + + nocontext bool + + // semantic data, lazily evaluated if requested by a backend to speed up + // the cases where semantic data is not needed. + semantic struct { + // uptodate tracks whether the fields below are up to date. + uptodate bool + root router.SemanticID + prevTree []router.SemanticNode + tree []router.SemanticNode + ids map[router.SemanticID]router.SemanticNode + } +} + +type semanticResult struct { + found bool + node router.SemanticNode } type callbacks struct { w *Window + d driver } // queue is an event.Queue implementation that distributes system events @@ -61,12 +93,6 @@ type queue struct { q router.Router } -// driverEvent is sent when a new native driver -// is available for the Window. -type driverEvent struct { - driver window.Driver -} - // Pre-allocate the ack event to avoid garbage. var ackEvent event.Event @@ -74,35 +100,37 @@ var ackEvent event.Event // options. The options are hints; the platform is free to // ignore or adjust them. // -// If the current program is running on iOS and Android, +// If the current program is running on iOS or Android, // NewWindow returns the window previously created by the // platform. // // Calling NewWindow more than once is not supported on // iOS, Android, WebAssembly. func NewWindow(options ...Option) *Window { - opts := &window.Options{ - Width: unit.Dp(800), - Height: unit.Dp(600), - Title: "Gio", - } - - for _, o := range options { - o(opts) + defaultOptions := []Option{ + Size(unit.Dp(800), unit.Dp(600)), + Title("Gio"), } + options = append(defaultOptions, options...) + var cnf Config + cnf.apply(unit.Metric{}, options) w := &Window{ - in: make(chan event.Event), - out: make(chan event.Event), - ack: make(chan struct{}), - invalidates: make(chan struct{}, 1), - frames: make(chan *op.Ops), - frameAck: make(chan struct{}), - driverFuncs: make(chan func()), - dead: make(chan struct{}), + out: make(chan event.Event), + immediateRedraws: make(chan struct{}, 0), + redraws: make(chan struct{}, 1), + scheduledRedraws: make(chan time.Time, 1), + frames: make(chan *op.Ops), + frameAck: make(chan struct{}), + driverFuncs: make(chan func(d driver), 1), + wakeups: make(chan struct{}, 1), + wakeupFuncs: make(chan func()), + dead: make(chan struct{}), + nocontext: cnf.CustomRenderer, } + w.semantic.ids = make(map[router.SemanticID]router.SemanticNode) w.callbacks.w = w - go w.run(opts) + go w.run(options) return w } @@ -111,141 +139,260 @@ func (w *Window) Events() <-chan event.Event { return w.out } -// update updates the Window. Paint operations updates the -// window contents, input operations declare input handlers, -// and so on. The supplied operations list completely replaces -// the window state from previous calls. +// update updates the window contents, input operations declare input handlers, +// and so on. The supplied operations list completely replaces the window state +// from previous calls. func (w *Window) update(frame *op.Ops) { w.frames <- frame <-w.frameAck } -func (w *Window) validateAndProcess(frameStart time.Time, size image.Point, sync bool, frame *op.Ops) error { +func (w *Window) validateAndProcess(d driver, frameStart time.Time, size image.Point, sync bool, frame *op.Ops) error { for { - if w.loop != nil { - if err := w.loop.Flush(); err != nil { + if w.gpu == nil && !w.nocontext { + var err error + if w.ctx == nil { + w.ctx, err = d.NewContext() + if err != nil { + return err + } + sync = true + } + } + if sync && w.ctx != nil { + if err := w.ctx.Refresh(); err != nil { + if errors.Is(err, errOutOfDate) { + // Surface couldn't be created for transient reasons. Skip + // this frame and wait for the next. + return nil + } w.destroyGPU() - if err == window.ErrDeviceLost { + if errors.Is(err, gpu.ErrDeviceLost) { continue } return err } } - if w.loop == nil { - var ctx window.Context - ctx, err := w.driver.NewContext() - if err != nil { + if w.gpu == nil && !w.nocontext { + if err := w.ctx.Lock(); err != nil { + w.destroyGPU() return err } - w.loop, err = newLoop(ctx) + gpu, err := gpu.New(w.ctx.API()) + w.ctx.Unlock() if err != nil { - ctx.Release() + w.destroyGPU() return err } + w.gpu = gpu } - w.processFrame(frameStart, size, frame) - if sync { - if err := w.loop.Flush(); err != nil { + if w.gpu != nil { + if err := w.render(frame, size); err != nil { + if errors.Is(err, errOutOfDate) { + // GPU surface needs refreshing. + sync = true + continue + } w.destroyGPU() - if err == window.ErrDeviceLost { + if errors.Is(err, gpu.ErrDeviceLost) { continue } return err } } + w.processFrame(d, frameStart, frame) return nil } } -func (w *Window) processFrame(frameStart time.Time, size image.Point, frame *op.Ops) { - sync := w.loop.Draw(size, frame) +func (w *Window) render(frame *op.Ops, viewport image.Point) error { + if err := w.ctx.Lock(); err != nil { + return err + } + defer w.ctx.Unlock() + if runtime.GOOS == "js" { + // Use transparent black when Gio is embedded, to allow mixing of Gio and + // foreign content below. + w.gpu.Clear(color.NRGBA{A: 0x00, R: 0x00, G: 0x00, B: 0x00}) + } else { + w.gpu.Clear(color.NRGBA{A: 0xff, R: 0xff, G: 0xff, B: 0xff}) + } + target, err := w.ctx.RenderTarget() + if err != nil { + return err + } + if err := w.gpu.Frame(frame, target, viewport); err != nil { + return err + } + return w.ctx.Present() +} + +func (w *Window) processFrame(d driver, frameStart time.Time, frame *op.Ops) { w.queue.q.Frame(frame) + for k := range w.semantic.ids { + delete(w.semantic.ids, k) + } + w.semantic.uptodate = false switch w.queue.q.TextInputState() { case router.TextInputOpen: - w.driver.ShowTextInput(true) + d.ShowTextInput(true) case router.TextInputClose: - w.driver.ShowTextInput(false) + d.ShowTextInput(false) + } + if hint, ok := w.queue.q.TextInputHint(); ok { + d.SetInputHint(hint) + } + if txt, ok := w.queue.q.WriteClipboard(); ok { + w.WriteClipboard(txt) + } + if w.queue.q.ReadClipboard() { + w.ReadClipboard() } - if w.queue.q.Profiling() { + if w.queue.q.Profiling() && w.gpu != nil { frameDur := time.Since(frameStart) frameDur = frameDur.Truncate(100 * time.Microsecond) q := 100 * time.Microsecond - timings := fmt.Sprintf("tot:%7s %s", frameDur.Round(q), w.loop.Summary()) - w.queue.q.Add(profile.Event{Timings: timings}) + timings := fmt.Sprintf("tot:%7s %s", frameDur.Round(q), w.gpu.Profile()) + w.queue.q.Queue(profile.Event{Timings: timings}) } if t, ok := w.queue.q.WakeupTime(); ok { w.setNextFrame(t) } - w.updateAnimation() - // Wait for the GPU goroutine to finish processing frame. - <-sync + w.updateAnimation(d) } -// Invalidate the window such that a FrameEvent will be generated -// immediately. If the window is inactive, the event is sent when the -// window becomes active. +// Invalidate the window such that a FrameEvent will be generated immediately. +// If the window is inactive, the event is sent when the window becomes active. +// +// Note that Invalidate is intended for externally triggered updates, such as a +// response from a network request. InvalidateOp is more efficient for animation +// and similar internal updates. +// // Invalidate is safe for concurrent use. func (w *Window) Invalidate() { select { - case w.invalidates <- struct{}{}: + case w.immediateRedraws <- struct{}{}: + return default: } + select { + case w.redraws <- struct{}{}: + w.wakeup() + default: + } +} + +// Option applies the options to the window. +func (w *Window) Option(opts ...Option) { + w.driverDefer(func(d driver) { + d.Configure(opts) + }) } // ReadClipboard initiates a read of the clipboard in the form -// of a system.ClipboardEvent. Multiple reads may be coalescedd +// of a clipboard.Event. Multiple reads may be coalesced // to a single event. func (w *Window) ReadClipboard() { - w.driverDo(func() { - w.driver.ReadClipboard() + w.driverDefer(func(d driver) { + d.ReadClipboard() }) } // WriteClipboard writes a string to the clipboard. func (w *Window) WriteClipboard(s string) { - w.driverDo(func() { - w.driver.WriteClipboard(s) + w.driverDefer(func(d driver) { + d.WriteClipboard(s) + }) +} + +// SetCursorName changes the current window cursor to name. +func (w *Window) SetCursorName(name pointer.CursorName) { + w.driverDefer(func(d driver) { + d.SetCursor(name) }) } // Close the window. The window's event loop should exit when it receives // system.DestroyEvent. // -// Currently, only macOS, Windows and X11 drivers implement this functionality, +// Currently, only macOS, Windows, X11 and Wayland drivers implement this functionality, // all others are stubbed. func (w *Window) Close() { - w.driverDo(func() { - w.driver.Close() + w.driverDefer(func(d driver) { + d.Close() }) } -// driverDo calls f as soon as the window has a valid driver attached, -// or does nothing if the window is destroyed while waiting. -func (w *Window) driverDo(f func()) { - go func() { - select { - case w.driverFuncs <- f: - case <-w.dead: - } - }() +// Maximize the window. +// Note: only implemented on Windows, macOS and X11. +func (w *Window) Maximize() { + w.driverDefer(func(d driver) { + d.Maximize() + }) } -func (w *Window) updateAnimation() { - animate := false - if w.delayedDraw != nil { - w.delayedDraw.Stop() - w.delayedDraw = nil +// Center the window. +// Note: only implemented on Windows, macOS and X11. +func (w *Window) Center() { + w.driverDefer(func(d driver) { + d.Center() + }) +} + +// Run f in the same thread as the native window event loop, and wait for f to +// return or the window to close. Run is guaranteed not to deadlock if it is +// invoked during the handling of a ViewEvent, system.FrameEvent, +// system.StageEvent; call Run in a separate goroutine to avoid deadlock in all +// other cases. +// +// Note that most programs should not call Run; configuring a Window with +// CustomRenderer is a notable exception. +func (w *Window) Run(f func()) { + done := make(chan struct{}) + w.driverDefer(func(d driver) { + defer close(done) + f() + }) + select { + case <-done: + case <-w.dead: } +} + +// driverDefer is like Run but can be run from any context. It doesn't wait +// for f to return. +func (w *Window) driverDefer(f func(d driver)) { + select { + case w.driverFuncs <- f: + w.wakeup() + case <-w.dead: + } +} + +func (w *Window) updateAnimation(d driver) { + animate := false if w.stage >= system.StageRunning && w.hasNextFrame { if dt := time.Until(w.nextFrame); dt <= 0 { animate = true } else { - w.delayedDraw = time.NewTimer(dt) + // Schedule redraw. + select { + case <-w.scheduledRedraws: + default: + } + w.scheduledRedraws <- w.nextFrame } } if animate != w.animating { w.animating = animate - w.driver.SetAnimating(animate) + d.SetAnimating(animate) + } +} + +func (w *Window) wakeup() { + select { + case w.wakeups <- struct{}{}: + default: } } @@ -256,164 +403,289 @@ func (w *Window) setNextFrame(at time.Time) { } } -func (c *callbacks) SetDriver(d window.Driver) { - c.Event(driverEvent{d}) +func (c *callbacks) SetDriver(d driver) { + c.d = d + var wakeup func() + if d != nil { + wakeup = d.Wakeup + } + c.w.wakeupFuncs <- wakeup } func (c *callbacks) Event(e event.Event) { - select { - case c.w.in <- e: - <-c.w.ack - case <-c.w.dead: + if c.d == nil { + panic("event while no driver active") } + c.w.processEvent(c.d, e) + c.w.updateState(c.d) +} + +// SemanticRoot returns the ID of the semantic root. +func (c *callbacks) SemanticRoot() router.SemanticID { + c.w.updateSemantics() + return c.w.semantic.root } -func (w *Window) waitAck() { - // Send a dummy event; when it gets through we - // know the application has processed the previous event. - w.out <- ackEvent +// LookupSemantic looks up a semantic node from an ID. The zero ID denotes the root. +func (c *callbacks) LookupSemantic(semID router.SemanticID) (router.SemanticNode, bool) { + c.w.updateSemantics() + n, found := c.w.semantic.ids[semID] + return n, found } -// Prematurely destroy the window and wait for the native window -// destroy event. -func (w *Window) destroy(err error) { - w.destroyGPU() - // Ack the current event. - w.ack <- struct{}{} - w.out <- system.DestroyEvent{Err: err} - close(w.dead) - for e := range w.in { - w.ack <- struct{}{} - if _, ok := e.(system.DestroyEvent); ok { +func (c *callbacks) AppendSemanticDiffs(diffs []router.SemanticID) []router.SemanticID { + c.w.updateSemantics() + if tree := c.w.semantic.prevTree; len(tree) > 0 { + c.w.collectSemanticDiffs(&diffs, c.w.semantic.prevTree[0]) + } + return diffs +} + +func (c *callbacks) SemanticAt(pos f32.Point) (router.SemanticID, bool) { + c.w.updateSemantics() + return c.w.queue.q.SemanticAt(pos) +} + +func (w *Window) waitAck(d driver) { + for { + select { + case f := <-w.driverFuncs: + f(d) + case w.out <- ackEvent: + // A dummy event went through, so we know the application has processed the previous event. return + case <-w.immediateRedraws: + // Invalidate was called during frame processing. + w.setNextFrame(time.Time{}) } } } func (w *Window) destroyGPU() { - if w.loop != nil { - w.loop.Release() - w.loop = nil + if w.gpu != nil { + w.ctx.Lock() + w.gpu.Release() + w.ctx.Unlock() + w.gpu = nil + } + if w.ctx != nil { + w.ctx.Release() + w.ctx = nil } } // waitFrame waits for the client to either call FrameEvent.Frame // or to continue event handling. It returns whether the client // called Frame or not. -func (w *Window) waitFrame() (*op.Ops, bool) { +func (w *Window) waitFrame(d driver) (*op.Ops, bool) { + for { + select { + case f := <-w.driverFuncs: + f(d) + case frame := <-w.frames: + // The client called FrameEvent.Frame. + return frame, true + case w.out <- ackEvent: + // The client ignored FrameEvent and continued processing + // events. + return nil, false + case <-w.immediateRedraws: + // Invalidate was called during frame processing. + w.setNextFrame(time.Time{}) + } + } +} + +// updateSemantics refreshes the semantics tree, the id to node map and the ids of +// updated nodes. +func (w *Window) updateSemantics() { + if w.semantic.uptodate { + return + } + w.semantic.uptodate = true + w.semantic.prevTree, w.semantic.tree = w.semantic.tree, w.semantic.prevTree + w.semantic.tree = w.queue.q.AppendSemantics(w.semantic.tree[:0]) + w.semantic.root = w.semantic.tree[0].ID + for _, n := range w.semantic.tree { + w.semantic.ids[n.ID] = n + } +} + +// collectSemanticDiffs traverses the previous semantic tree, noting changed nodes. +func (w *Window) collectSemanticDiffs(diffs *[]router.SemanticID, n router.SemanticNode) { + newNode, exists := w.semantic.ids[n.ID] + // Ignore deleted nodes, as their disappearance will be reported through an + // ancestor node. + if !exists { + return + } + diff := newNode.Desc != n.Desc || len(n.Children) != len(newNode.Children) + for i, ch := range n.Children { + if !diff { + newCh := newNode.Children[i] + diff = ch.ID != newCh.ID + } + w.collectSemanticDiffs(diffs, ch) + } + if diff { + *diffs = append(*diffs, n.ID) + } +} + +func (w *Window) updateState(d driver) { + for { + select { + case f := <-w.driverFuncs: + f(d) + case <-w.redraws: + w.setNextFrame(time.Time{}) + w.updateAnimation(d) + default: + return + } + } +} + +func (w *Window) processEvent(d driver, e event.Event) { select { - case frame := <-w.frames: - // The client called FrameEvent.Frame. - return frame, true - case w.out <- ackEvent: - // The client ignored FrameEvent and continued processing - // events. - return nil, false + case <-w.dead: + return + default: + } + switch e2 := e.(type) { + case system.StageEvent: + if e2.Stage < system.StageRunning { + if w.gpu != nil { + w.ctx.Lock() + w.gpu.Release() + w.gpu = nil + w.ctx.Unlock() + } + } + w.stage = e2.Stage + w.updateAnimation(d) + w.out <- e + w.waitAck(d) + case frameEvent: + if e2.Size == (image.Point{}) { + panic(errors.New("internal error: zero-sized Draw")) + } + if w.stage < system.StageRunning { + // No drawing if not visible. + break + } + frameStart := time.Now() + w.hasNextFrame = false + e2.Frame = w.update + e2.Queue = &w.queue + w.out <- e2.FrameEvent + frame, gotFrame := w.waitFrame(d) + err := w.validateAndProcess(d, frameStart, e2.Size, e2.Sync, frame) + if gotFrame { + // We're done with frame, let the client continue. + w.frameAck <- struct{}{} + } + if err != nil { + w.destroyGPU() + w.out <- system.DestroyEvent{Err: err} + close(w.dead) + close(w.out) + break + } + w.updateCursor() + case *system.CommandEvent: + w.out <- e + w.waitAck(d) + case system.DestroyEvent: + w.destroyGPU() + w.out <- e2 + close(w.dead) + close(w.out) + case ViewEvent: + w.out <- e2 + w.waitAck(d) + case wakeupEvent: + case event.Event: + if w.queue.q.Queue(e2) { + w.setNextFrame(time.Time{}) + w.updateAnimation(d) + } + w.updateCursor() + w.out <- e } } -func (w *Window) run(opts *window.Options) { - defer close(w.in) - defer close(w.out) - if err := window.NewWindow(&w.callbacks, opts); err != nil { +func (w *Window) run(options []Option) { + if err := newWindow(&w.callbacks, options); err != nil { w.out <- system.DestroyEvent{Err: err} + close(w.dead) + close(w.out) return } + var wakeup func() + var timer *time.Timer for { - var driverFuncs chan func() - if w.driver != nil { - driverFuncs = w.driverFuncs - } - var timer <-chan time.Time - if w.delayedDraw != nil { - timer = w.delayedDraw.C + var ( + wakeups <-chan struct{} + timeC <-chan time.Time + ) + if wakeup != nil { + wakeups = w.wakeups + if timer != nil { + timeC = timer.C + } } select { - case <-timer: - w.setNextFrame(time.Time{}) - w.updateAnimation() - case <-w.invalidates: - w.setNextFrame(time.Time{}) - w.updateAnimation() - case f := <-driverFuncs: - f() - case e := <-w.in: - switch e2 := e.(type) { - case system.StageEvent: - if w.loop != nil { - if e2.Stage < system.StageRunning { - w.destroyGPU() - } else { - w.loop.Refresh() - } - } - w.stage = e2.Stage - w.updateAnimation() - w.out <- e - w.waitAck() - case window.FrameEvent: - if e2.Size == (image.Point{}) { - panic(errors.New("internal error: zero-sized Draw")) - } - if w.stage < system.StageRunning { - // No drawing if not visible. - break - } - frameStart := time.Now() - w.hasNextFrame = false - e2.Frame = w.update - e2.Queue = &w.queue - w.out <- e2.FrameEvent - if w.loop != nil { - if e2.Sync { - w.loop.Refresh() - } - } - frame, gotFrame := w.waitFrame() - err := w.validateAndProcess(frameStart, e2.Size, e2.Sync, frame) - if gotFrame { - // We're done with frame, let the client continue. - w.frameAck <- struct{}{} - } - if err != nil { - w.destroyGPU() - w.destroy(err) - return - } - case *system.CommandEvent: - w.out <- e - w.waitAck() - case driverEvent: - w.driver = e2.driver - case system.DestroyEvent: - w.destroyGPU() - w.out <- e2 - w.ack <- struct{}{} - return - case event.Event: - if w.queue.q.Add(e2) { - w.setNextFrame(time.Time{}) - w.updateAnimation() - } - w.out <- e + case t := <-w.scheduledRedraws: + if timer != nil { + timer.Stop() } - w.ack <- struct{}{} + timer = time.NewTimer(time.Until(t)) + case <-w.dead: + return + case <-timeC: + select { + case w.redraws <- struct{}{}: + wakeup() + default: + } + case <-wakeups: + wakeup() + case wakeup = <-w.wakeupFuncs: } } } +func (w *Window) updateCursor() { + if c := w.queue.q.Cursor(); c != w.cursor { + w.cursor = c + w.SetCursorName(c) + } +} + +// Raise requests that the platform bring this window to the top of all open windows. +// Some platforms do not allow this except under certain circumstances, such as when +// a window from the same application already has focus. If the platform does not +// support it, this method will do nothing. +func (w *Window) Raise() { + w.driverDefer(func(d driver) { + d.Raise() + }) +} + func (q *queue) Events(k event.Tag) []event.Event { return q.q.Events(k) } // Title sets the title of the window. func Title(t string) Option { - return func(opts *window.Options) { - opts.Title = t + return func(_ unit.Metric, cnf *Config) { + cnf.Title = t } } -// Size sets the size of the window. +// Size sets the size of the window. The option is ignored +// in Fullscreen mode. func Size(w, h unit.Value) Option { if w.V <= 0 { panic("width must be larger than or equal to 0") @@ -421,9 +693,11 @@ func Size(w, h unit.Value) Option { if h.V <= 0 { panic("height must be larger than or equal to 0") } - return func(opts *window.Options) { - opts.Width = w - opts.Height = h + return func(m unit.Metric, cnf *Config) { + cnf.Size = image.Point{ + X: m.Px(w), + Y: m.Px(h), + } } } @@ -435,9 +709,11 @@ func MaxSize(w, h unit.Value) Option { if h.V <= 0 { panic("height must be larger than or equal to 0") } - return func(opts *window.Options) { - opts.MaxWidth = w - opts.MaxHeight = h + return func(m unit.Metric, cnf *Config) { + cnf.MaxSize = image.Point{ + X: m.Px(w), + Y: m.Px(h), + } } } @@ -449,10 +725,32 @@ func MinSize(w, h unit.Value) Option { if h.V <= 0 { panic("height must be larger than or equal to 0") } - return func(opts *window.Options) { - opts.MinWidth = w - opts.MinHeight = h + return func(m unit.Metric, cnf *Config) { + cnf.MinSize = image.Point{ + X: m.Px(w), + Y: m.Px(h), + } } } -func (driverEvent) ImplementsEvent() {} +// StatusColor sets the color of the Android status bar. +func StatusColor(color color.NRGBA) Option { + return func(_ unit.Metric, cnf *Config) { + cnf.StatusColor = color + } +} + +// NavigationColor sets the color of the navigation bar on Android, or the address bar in browsers. +func NavigationColor(color color.NRGBA) Option { + return func(_ unit.Metric, cnf *Config) { + cnf.NavigationColor = color + } +} + +// CustomRenderer controls whether the window contents is +// rendered by the client. If true, no GPU context is created. +func CustomRenderer(custom bool) Option { + return func(_ unit.Metric, cnf *Config) { + cnf.CustomRenderer = custom + } +} diff --git a/vendor/gioui.org/cpu/LICENSE b/vendor/gioui.org/cpu/LICENSE new file mode 100644 index 0000000..81f4733 --- /dev/null +++ b/vendor/gioui.org/cpu/LICENSE @@ -0,0 +1,63 @@ +This project is provided under the terms of the UNLICENSE or +the MIT license denoted by the following SPDX identifier: + +SPDX-License-Identifier: Unlicense OR MIT + +You may use the project under the terms of either license. + +Both licenses are reproduced below. + +---- +The MIT License (MIT) + +Copyright (c) 2019 The Gio authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +--- + + + +--- +The UNLICENSE + +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to <https://unlicense.org/> +--- diff --git a/vendor/gioui.org/cpu/README.md b/vendor/gioui.org/cpu/README.md new file mode 100644 index 0000000..4244fe0 --- /dev/null +++ b/vendor/gioui.org/cpu/README.md @@ -0,0 +1,25 @@ +# Compile and run compute programs on CPU + +This projects contains the compiler for turning Vulkan SPIR-V compute shaders +into binaries for arm64, arm or amd64, using +[SwiftShader](https://github.com/eliasnaur/swiftshader) with a few +modifications. A runtime implemented in C and Go is available for running the +resulting binaries. + +The primary use is to support a CPU-based rendering fallback for +[Gio](https://gioui.org). In particular, the `gioui.org/shader/piet` package +contains arm, arm64, amd64 binaries for +[piet-gpu](https://github.com/linebender/piet-gpu). + +# Compiling and running shaders + +The `init.sh` script clones the modifed SwiftShader projects and builds it for +64-bit and 32-bit. SwiftShader is not designed to cross-compile which is why a +32-bit build is needed to compile shaders for arm. + +The `example/run.sh` script demonstrates compiling and running a simple compute +program. + +## Issues and contributions + +See the [Gio contribution guide](https://gioui.org/doc/contribute). diff --git a/vendor/gioui.org/cpu/abi.h b/vendor/gioui.org/cpu/abi.h new file mode 100644 index 0000000..365d936 --- /dev/null +++ b/vendor/gioui.org/cpu/abi.h @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +#define ALIGN(bytes, type) type __attribute__((aligned(bytes))) + +typedef ALIGN(8, uint8_t) byte8[8]; +typedef ALIGN(8, uint16_t) word4[4]; +typedef ALIGN(4, uint32_t) dword; +typedef ALIGN(16, uint32_t) dword4[4]; +typedef ALIGN(8, uint64_t) qword; +typedef ALIGN(16, uint64_t) qword2[2]; +typedef ALIGN(16, unsigned int) uint4[4]; +typedef ALIGN(8, uint32_t) dword2[2]; +typedef ALIGN(8, unsigned short) ushort4[4]; +typedef ALIGN(16, float) float4[4]; +typedef ALIGN(16, int) int4[4]; + +typedef unsigned short half; + +typedef unsigned char bool; + +enum { + MAX_BOUND_DESCRIPTOR_SETS = 4, + MAX_DESCRIPTOR_SET_UNIFORM_BUFFERS_DYNAMIC = 8, + MAX_DESCRIPTOR_SET_STORAGE_BUFFERS_DYNAMIC = 4, + MAX_DESCRIPTOR_SET_COMBINED_BUFFERS_DYNAMIC = + MAX_DESCRIPTOR_SET_UNIFORM_BUFFERS_DYNAMIC + + MAX_DESCRIPTOR_SET_STORAGE_BUFFERS_DYNAMIC, + MAX_PUSH_CONSTANT_SIZE = 128, + + MIN_STORAGE_BUFFER_OFFSET_ALIGNMENT = 256, + + REQUIRED_MEMORY_ALIGNMENT = 16, + + SIMD_WIDTH = 4, +}; + +struct image_descriptor { + ALIGN(16, void *ptr); + int width; + int height; + int depth; + int row_pitch_bytes; + int slice_pitch_bytes; + int sample_pitch_bytes; + int sample_count; + int size_in_bytes; + + void *stencil_ptr; + int stencil_row_pitch_bytes; + int stencil_slice_pitch_bytes; + int stencil_sample_pitch_bytes; + + // TODO: unused? + void *memoryOwner; +}; + +struct buffer_descriptor { + ALIGN(16, void *ptr); + int size_in_bytes; + int robustness_size; +}; + +struct program_data { + uint8_t *descriptor_sets[MAX_BOUND_DESCRIPTOR_SETS]; + uint32_t descriptor_dynamic_offsets[MAX_DESCRIPTOR_SET_COMBINED_BUFFERS_DYNAMIC]; + uint4 num_workgroups; + uint4 workgroup_size; + uint32_t invocations_per_subgroup; + uint32_t subgroups_per_workgroup; + uint32_t invocations_per_workgroup; + unsigned char push_constants[MAX_PUSH_CONSTANT_SIZE]; + // Unused. + void *constants; +}; + +typedef int32_t yield_result; + +typedef void * coroutine; + +typedef coroutine (*routine_begin)(struct program_data *data, + int32_t workgroupX, + int32_t workgroupY, + int32_t workgroupZ, + void *workgroupMemory, + int32_t firstSubgroup, + int32_t subgroupCount); + +typedef bool (*routine_await)(coroutine r, yield_result *res); + +typedef void (*routine_destroy)(coroutine r); + diff --git a/vendor/gioui.org/cpu/driver.go b/vendor/gioui.org/cpu/driver.go new file mode 100644 index 0000000..d156e2b --- /dev/null +++ b/vendor/gioui.org/cpu/driver.go @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build linux && (arm64 || arm || amd64) +// +build linux +// +build arm64 arm amd64 + +package cpu + +/* +#cgo CFLAGS: -std=c11 -D_POSIX_C_SOURCE=200112L + +#include <stdint.h> +#include <stdlib.h> +#include "abi.h" +#include "runtime.h" +*/ +import "C" +import ( + "unsafe" +) + +type ( + BufferDescriptor = C.struct_buffer_descriptor + ImageDescriptor = C.struct_image_descriptor + SamplerDescriptor = C.struct_sampler_descriptor + + DispatchContext = C.struct_dispatch_context + ThreadContext = C.struct_thread_context + ProgramInfo = C.struct_program_info +) + +const Supported = true + +func NewBuffer(size int) BufferDescriptor { + return C.alloc_buffer(C.size_t(size)) +} + +func (d *BufferDescriptor) Data() []byte { + return (*(*[1 << 30]byte)(d.ptr))[:d.size_in_bytes:d.size_in_bytes] +} + +func (d *BufferDescriptor) Free() { + if d.ptr != nil { + C.free(d.ptr) + } + *d = BufferDescriptor{} +} + +func NewImageRGBA(width, height int) ImageDescriptor { + return C.alloc_image_rgba(C.int(width), C.int(height)) +} + +func (d *ImageDescriptor) Data() []byte { + return (*(*[1 << 30]byte)(d.ptr))[:d.size_in_bytes:d.size_in_bytes] +} + +func (d *ImageDescriptor) Free() { + if d.ptr != nil { + C.free(d.ptr) + } + *d = ImageDescriptor{} +} + +func NewDispatchContext() *DispatchContext { + return C.alloc_dispatch_context() +} + +func (c *DispatchContext) Free() { + C.free_dispatch_context(c) +} + +func (c *DispatchContext) Prepare(numThreads int, prog *ProgramInfo, descSet unsafe.Pointer, x, y, z int) { + C.prepare_dispatch(c, C.int(numThreads), prog, (*C.uint8_t)(descSet), C.int(x), C.int(y), C.int(z)) +} + +func (c *DispatchContext) Dispatch(threadIdx int, ctx *ThreadContext) { + C.dispatch_thread(c, C.int(threadIdx), ctx) +} + +func NewThreadContext() *ThreadContext { + return C.alloc_thread_context() +} + +func (c *ThreadContext) Free() { + C.free_thread_context(c) +} diff --git a/vendor/gioui.org/cpu/driver_nosupport.go b/vendor/gioui.org/cpu/driver_nosupport.go new file mode 100644 index 0000000..3a118f2 --- /dev/null +++ b/vendor/gioui.org/cpu/driver_nosupport.go @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build !(linux && (arm64 || arm || amd64)) +// +build !linux !arm64,!arm,!amd64 + +package cpu + +import "unsafe" + +type ( + BufferDescriptor struct{} + ImageDescriptor struct{} + SamplerDescriptor struct{} + + DispatchContext struct{} + ThreadContext struct{} + ProgramInfo struct{} +) + +const Supported = false + +func NewBuffer(size int) BufferDescriptor { + panic("unsupported") +} + +func (d *BufferDescriptor) Data() []byte { + panic("unsupported") +} + +func (d *BufferDescriptor) Free() { +} + +func NewImageRGBA(width, height int) ImageDescriptor { + panic("unsupported") +} + +func (d *ImageDescriptor) Data() []byte { + panic("unsupported") +} + +func (d *ImageDescriptor) Free() { +} + +func NewDispatchContext() *DispatchContext { + panic("unsupported") +} + +func (c *DispatchContext) Free() { +} + +func (c *DispatchContext) Prepare(numThreads int, prog *ProgramInfo, descSet unsafe.Pointer, x, y, z int) { + panic("unsupported") +} + +func (c *DispatchContext) Dispatch(threadIdx int, ctx *ThreadContext) { + panic("unsupported") +} + +func NewThreadContext() *ThreadContext { + panic("unsupported") +} + +func (c *ThreadContext) Free() { +} diff --git a/vendor/gioui.org/cpu/embed.go b/vendor/gioui.org/cpu/embed.go new file mode 100644 index 0000000..9d3b944 --- /dev/null +++ b/vendor/gioui.org/cpu/embed.go @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package cpu + +import _ "embed" + +//go:embed abi.h +var ABIH []byte + +//go:embed runtime.h +var RuntimeH []byte diff --git a/vendor/gioui.org/cpu/go.mod b/vendor/gioui.org/cpu/go.mod new file mode 100644 index 0000000..46709a0 --- /dev/null +++ b/vendor/gioui.org/cpu/go.mod @@ -0,0 +1,3 @@ +module gioui.org/cpu + +go 1.17 diff --git a/vendor/gioui.org/cpu/go.sum b/vendor/gioui.org/cpu/go.sum new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/vendor/gioui.org/cpu/go.sum diff --git a/vendor/gioui.org/cpu/init.sh b/vendor/gioui.org/cpu/init.sh new file mode 100644 index 0000000..f0f0a9c --- /dev/null +++ b/vendor/gioui.org/cpu/init.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +# SPDX-License-Identifier: Unlicense OR MIT + +set -e + +cd ~/.cache +git clone https://github.com/eliasnaur/swiftshader +cd swiftshader + +# 32-bit build +cp -a build build.32bit +cd build.32bit +CXX=clang++ CC=clang CFLAGS=-m32 CXXFLAGS=-m32 cmake -DREACTOR_EMIT_ASM_FILE=true -DSWIFTSHADER_BUILD_PVR=false -DSWIFTSHADER_BUILD_TESTS=false -DSWIFTSHADER_BUILD_GLESv2=false -DSWIFTSHADER_BUILD_EGL=false -DSWIFTSHADER_BUILD_ANGLE=false .. +cmake --build . --parallel 4 +cd .. + +# 64-bit build +cp -a build build.64bit +cd build.64bit +CXX=clang++ CC=clang cmake -DREACTOR_EMIT_ASM_FILE=true -DSWIFTSHADER_BUILD_PVR=false -DSWIFTSHADER_BUILD_TESTS=false -DSWIFTSHADER_BUILD_GLESv2=false -DSWIFTSHADER_BUILD_EGL=false -DSWIFTSHADER_BUILD_ANGLE=false .. +cmake --build . --parallel 4 +cd .. diff --git a/vendor/gioui.org/cpu/runtime.c b/vendor/gioui.org/cpu/runtime.c new file mode 100644 index 0000000..f7f6108 --- /dev/null +++ b/vendor/gioui.org/cpu/runtime.c @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build linux && (arm64 || arm || amd64) +// +build linux +// +build arm64 arm amd64 + +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <math.h> +#include <stdlib.h> +#include <assert.h> +#include "abi.h" +#include "runtime.h" +#include "_cgo_export.h" + +// coroutines is a FIFO queue of coroutines implemented as a circular +// buffer. +struct coroutines { + coroutine *routines; + // start and end indexes into routines. + unsigned int start; + unsigned int end; + // cap is the capacity of routines. + unsigned int cap; +}; + +struct dispatch_context { + // descriptor_set is the aligned storage for the descriptor set. + void *descriptor_set; + int desc_set_size; + + int nthreads; + bool has_cbarriers; + size_t memory_size; + // Program entrypoints. + routine_begin begin; + routine_await await; + routine_destroy destroy; + + struct program_data data; +}; + +struct thread_context { + struct coroutines routines; + size_t memory_size; + uint8_t *memory; +}; + +static void *malloc_align(size_t alignment, size_t size) { + void *ptr; + int ret = posix_memalign(&ptr, alignment, size); + assert(ret == 0); + return ptr; +} + +static void coroutines_dump(struct coroutines *routines) { + fprintf(stderr, "s: %d e: %d c: %d [", routines->start, routines->end, routines->cap); + unsigned int i = routines->start; + while (i != routines->end) { + fprintf(stderr, "%p,", routines->routines[routines->start]); + i = (i + 1)%routines->cap; + } + fprintf(stderr, "]\n"); +} + +static void coroutines_push(struct coroutines *routines, coroutine r) { + unsigned int next = routines->end + 1; + if (next >= routines->cap) { + next = 0; + } + if (next == routines->start) { + unsigned int newcap = routines->cap*2; + if (newcap < 10) { + newcap = 10; + } + routines->routines = realloc(routines->routines, newcap*sizeof(coroutine)); + // Move elements wrapped around the old cap to the newly allocated space. + if (routines->end < routines->start) { + unsigned int nelems = routines->end; + unsigned int max = newcap - routines->cap; + // We doubled the space above, so we can assume enough room. + assert(nelems <= max); + memmove(&routines->routines[routines->cap], &routines->routines[0], nelems*sizeof(coroutine)); + routines->end += routines->cap; + } + routines->cap = newcap; + next = (routines->end + 1)%routines->cap; + } + routines->routines[routines->end] = r; + routines->end = next; +} + +static bool coroutines_pop(struct coroutines *routines, coroutine *r) { + if (routines->start == routines->end) { + return 0; + } + *r = routines->routines[routines->start]; + routines->start = (routines->start + 1)%routines->cap; + return 1; +} + +static void coroutines_free(struct coroutines *routines) { + if (routines->routines != NULL) { + free(routines->routines); + } + struct coroutines clr = { 0 }; *routines = clr; +} + +struct dispatch_context *alloc_dispatch_context(void) { + struct dispatch_context *c = malloc(sizeof(*c)); + assert(c != NULL); + struct dispatch_context clr = { 0 }; *c = clr; + return c; +} + +void free_dispatch_context(struct dispatch_context *c) { + if (c->descriptor_set != NULL) { + free(c->descriptor_set); + c->descriptor_set = NULL; + } +} + +struct thread_context *alloc_thread_context(void) { + struct thread_context *c = malloc(sizeof(*c)); + assert(c != NULL); + struct thread_context clr = { 0 }; *c = clr; + return c; +} + +void free_thread_context(struct thread_context *c) { + if (c->memory != NULL) { + free(c->memory); + } + coroutines_free(&c->routines); + struct thread_context clr = { 0 }; *c = clr; +} + +struct buffer_descriptor alloc_buffer(size_t size) { + void *buf = malloc_align(MIN_STORAGE_BUFFER_OFFSET_ALIGNMENT, size); + struct buffer_descriptor desc = { + .ptr = buf, + .size_in_bytes = size, + .robustness_size = size, + }; + return desc; +} + +struct image_descriptor alloc_image_rgba(int width, int height) { + size_t size = width*height*4; + size = (size + 16 - 1)&~(16 - 1); + void *storage = malloc_align(REQUIRED_MEMORY_ALIGNMENT, size); + struct image_descriptor desc = { 0 }; + desc.ptr = storage; + desc.width = width; + desc.height = height; + desc.depth = 1; + desc.row_pitch_bytes = width*4; + desc.slice_pitch_bytes = size; + desc.sample_pitch_bytes = size; + desc.sample_count = 1; + desc.size_in_bytes = size; + return desc; +} + +void prepare_dispatch(struct dispatch_context *ctx, int nthreads, struct program_info *info, uint8_t *desc_set, int ngroupx, int ngroupy, int ngroupz) { + if (ctx->desc_set_size < info->desc_set_size) { + if (ctx->descriptor_set != NULL) { + free(ctx->descriptor_set); + } + ctx->descriptor_set = malloc_align(16, info->desc_set_size); + ctx->desc_set_size = info->desc_set_size; + } + memcpy(ctx->descriptor_set, desc_set, info->desc_set_size); + + int invocations_per_subgroup = SIMD_WIDTH; + int invocations_per_workgroup = info->workgroup_size_x * info->workgroup_size_y * info->workgroup_size_z; + int subgroups_per_workgroup = (invocations_per_workgroup + invocations_per_subgroup - 1) / invocations_per_subgroup; + + ctx->has_cbarriers = info->has_cbarriers; + ctx->begin = info->begin; + ctx->await = info->await; + ctx->destroy = info->destroy; + ctx->nthreads = nthreads; + ctx->memory_size = info->min_memory_size; + + ctx->data.workgroup_size[0] = info->workgroup_size_x; + ctx->data.workgroup_size[1] = info->workgroup_size_y; + ctx->data.workgroup_size[2] = info->workgroup_size_z; + ctx->data.num_workgroups[0] = ngroupx; + ctx->data.num_workgroups[1] = ngroupy; + ctx->data.num_workgroups[2] = ngroupz; + ctx->data.invocations_per_subgroup = invocations_per_subgroup; + ctx->data.invocations_per_workgroup = invocations_per_workgroup; + ctx->data.subgroups_per_workgroup = subgroups_per_workgroup; + ctx->data.descriptor_sets[0] = ctx->descriptor_set; +} + +void dispatch_thread(struct dispatch_context *ctx, int thread_idx, struct thread_context *thread) { + if (thread->memory_size < ctx->memory_size) { + if (thread->memory != NULL) { + free(thread->memory); + } + // SwiftShader doesn't seem to align shared memory. However, better safe + // than subtle errors. Note that the program info generator pads + // memory_size to ensure space for alignment. + thread->memory = malloc_align(16, ctx->memory_size); + thread->memory_size = ctx->memory_size; + } + uint8_t *memory = thread->memory; + + struct program_data *data = &ctx->data; + + int sx = data->num_workgroups[0]; + int sy = data->num_workgroups[1]; + int sz = data->num_workgroups[2]; + int ngroups = sx * sy * sz; + + for (int i = thread_idx; i < ngroups; i += ctx->nthreads) { + int group_id = i; + int z = group_id / (sx * sy); + group_id -= z * sx * sy; + int y = group_id / sx; + group_id -= y * sx; + int x = group_id; + if (ctx->has_cbarriers) { + for (int subgroup = 0; subgroup < data->subgroups_per_workgroup; subgroup++) { + coroutine r = ctx->begin(data, x, y, z, memory, subgroup, 1); + coroutines_push(&thread->routines, r); + } + } else { + coroutine r = ctx->begin(data, x, y, z, memory, 0, data->subgroups_per_workgroup); + coroutines_push(&thread->routines, r); + } + coroutine r; + while (coroutines_pop(&thread->routines, &r)) { + yield_result res; + if (ctx->await(r, &res)) { + coroutines_push(&thread->routines, r); + } else { + ctx->destroy(r); + } + } + } +} diff --git a/vendor/gioui.org/cpu/runtime.h b/vendor/gioui.org/cpu/runtime.h new file mode 100644 index 0000000..cfae912 --- /dev/null +++ b/vendor/gioui.org/cpu/runtime.h @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +#define ATTR_HIDDEN __attribute__ ((visibility ("hidden"))) + +// program_info contains constant parameters for a program. +struct program_info { + // MinMemorySize is the minimum size of memory passed to dispatch. + size_t min_memory_size; + // has_cbarriers is 1 when the program contains control barriers. + bool has_cbarriers; + // desc_set_size is the size of the first descriptor set for the program. + size_t desc_set_size; + int workgroup_size_x; + int workgroup_size_y; + int workgroup_size_z; + // Program entrypoints. + routine_begin begin; + routine_await await; + routine_destroy destroy; +}; + +// dispatch_context contains the information a program dispatch. +struct dispatch_context; + +// thread_context contains the working memory of a batch. It may be +// reused, but not concurrently. +struct thread_context; + +extern struct buffer_descriptor alloc_buffer(size_t size) ATTR_HIDDEN; +extern struct image_descriptor alloc_image_rgba(int width, int height) ATTR_HIDDEN; + +extern struct dispatch_context *alloc_dispatch_context(void) ATTR_HIDDEN; + +extern void free_dispatch_context(struct dispatch_context *c) ATTR_HIDDEN; + +extern struct thread_context *alloc_thread_context(void) ATTR_HIDDEN; + +extern void free_thread_context(struct thread_context *c) ATTR_HIDDEN; + +// prepare_dispatch initializes ctx to run a dispatch of a program distributed +// among nthreads threads. +extern void prepare_dispatch(struct dispatch_context *ctx, int nthreads, struct program_info *info, uint8_t *desc_set, int ngroupx, int ngroupy, int ngroupz) ATTR_HIDDEN; + +// dispatch_batch executes a dispatch batch. +extern void dispatch_thread(struct dispatch_context *ctx, int thread_idx, struct thread_context *thread) ATTR_HIDDEN; diff --git a/vendor/gioui.org/f32/affine.go b/vendor/gioui.org/f32/affine.go index 44e914b..667f7e9 100644 --- a/vendor/gioui.org/f32/affine.go +++ b/vendor/gioui.org/f32/affine.go @@ -3,6 +3,7 @@ package f32 import ( + "fmt" "math" ) @@ -11,7 +12,7 @@ import ( type Affine2D struct { // in order to make the zero value of Affine2D represent the identity // transform we store it with the identity matrix subtracted, that is - // if the actual transformaiton matrix is: + // if the actual transformation matrix is: // [sx, hx, ox] // [hy, sy, oy] // [ 0, 0, 1] @@ -24,9 +25,9 @@ type Affine2D struct { // in row major order. The rows are: [sx, hx, ox], [hy, sy, oy], [0, 0, 1]. func NewAffine2D(sx, hx, ox, hy, sy, oy float32) Affine2D { return Affine2D{ - a: sx, b: hx, c: ox, - d: hy, e: sy, f: oy, - }.encode() + a: sx - 1, b: hx, c: ox, + d: hy, e: sy - 1, f: oy, + } } // Offset the transformation. @@ -69,14 +70,13 @@ func (a Affine2D) Shear(origin Point, radiansX, radiansY float32) Affine2D { // Mul returns A*B. func (A Affine2D) Mul(B Affine2D) (r Affine2D) { - A, B = A.decode(), B.decode() - r.a = A.a*B.a + A.b*B.d - r.b = A.a*B.b + A.b*B.e - r.c = A.a*B.c + A.b*B.f + A.c - r.d = A.d*B.a + A.e*B.d - r.e = A.d*B.b + A.e*B.e - r.f = A.d*B.c + A.e*B.f + A.f - return r.encode() + r.a = (A.a+1)*(B.a+1) + A.b*B.d - 1 + r.b = (A.a+1)*B.b + A.b*(B.e+1) + r.c = (A.a+1)*B.c + A.b*B.f + A.c + r.d = A.d*(B.a+1) + (A.e+1)*B.d + r.e = A.d*B.b + (A.e+1)*(B.e+1) - 1 + r.f = A.d*B.c + (A.e+1)*B.f + A.f + return r } // Invert the transformation. Note that if the matrix is close to singular @@ -85,70 +85,59 @@ func (a Affine2D) Invert() Affine2D { if a.a == 0 && a.b == 0 && a.d == 0 && a.e == 0 { return Affine2D{a: 0, b: 0, c: -a.c, d: 0, e: 0, f: -a.f} } - a = a.decode() + a.a += 1 + a.e += 1 det := a.a*a.e - a.b*a.d a.a, a.e = a.e/det, a.a/det a.b, a.d = -a.b/det, -a.d/det temp := a.c a.c = -a.a*a.c - a.b*a.f a.f = -a.d*temp - a.e*a.f - return a.encode() + a.a -= 1 + a.e -= 1 + return a } // Transform p by returning a*p. func (a Affine2D) Transform(p Point) Point { - a = a.decode() return Point{ - X: p.X*a.a + p.Y*a.b + a.c, - Y: p.X*a.d + p.Y*a.e + a.f, + X: p.X*(a.a+1) + p.Y*a.b + a.c, + Y: p.X*a.d + p.Y*(a.e+1) + a.f, } } // Elems returns the matrix elements of the transform in row-major order. The // rows are: [sx, hx, ox], [hy, sy, oy], [0, 0, 1]. func (a Affine2D) Elems() (sx, hx, ox, hy, sy, oy float32) { - a = a.decode() - return a.a, a.b, a.c, a.d, a.e, a.f -} - -func (a Affine2D) encode() Affine2D { - // since we store with identity matrix subtracted - a.a -= 1 - a.e -= 1 - return a -} - -func (a Affine2D) decode() Affine2D { - // since we store with identity matrix subtracted - a.a += 1 - a.e += 1 - return a + return a.a + 1, a.b, a.c, a.d, a.e + 1, a.f } func (a Affine2D) scale(factor Point) Affine2D { - a = a.decode() return Affine2D{ - a.a * factor.X, a.b * factor.X, a.c * factor.X, - a.d * factor.Y, a.e * factor.Y, a.f * factor.Y, - }.encode() + (a.a+1)*factor.X - 1, a.b * factor.X, a.c * factor.X, + a.d * factor.Y, (a.e+1)*factor.Y - 1, a.f * factor.Y, + } } func (a Affine2D) rotate(radians float32) Affine2D { sin, cos := math.Sincos(float64(radians)) s, c := float32(sin), float32(cos) - a = a.decode() return Affine2D{ - a.a*c - a.d*s, a.b*c - a.e*s, a.c*c - a.f*s, - a.a*s + a.d*c, a.b*s + a.e*c, a.c*s + a.f*c, - }.encode() + (a.a+1)*c - a.d*s - 1, a.b*c - (a.e+1)*s, a.c*c - a.f*s, + (a.a+1)*s + a.d*c, a.b*s + (a.e+1)*c - 1, a.c*s + a.f*c, + } } func (a Affine2D) shear(radiansX, radiansY float32) Affine2D { tx := float32(math.Tan(float64(radiansX))) ty := float32(math.Tan(float64(radiansY))) - a = a.decode() return Affine2D{ - a.a + a.d*tx, a.b + a.e*tx, a.c + a.f*tx, - a.a*ty + a.d, a.b*ty + a.e, a.f*ty + a.f, - }.encode() + (a.a + 1) + a.d*tx - 1, a.b + (a.e+1)*tx, a.c + a.f*tx, + (a.a+1)*ty + a.d, a.b*ty + (a.e + 1) - 1, a.f*ty + a.f, + } +} + +func (a Affine2D) String() string { + sx, hx, ox, hy, sy, oy := a.Elems() + return fmt.Sprintf("[[%f %f %f] [%f %f %f]]", sx, hx, ox, hy, sy, oy) } diff --git a/vendor/gioui.org/f32/f32.go b/vendor/gioui.org/f32/f32.go index 798e3bf..54882cb 100644 --- a/vendor/gioui.org/f32/f32.go +++ b/vendor/gioui.org/f32/f32.go @@ -66,6 +66,17 @@ func (p Point) Mul(s float32) Point { return Point{X: p.X * s, Y: p.Y * s} } +// Div returns the vector p/s. +func (p Point) Div(s float32) Point { + return Point{X: p.X / s, Y: p.Y / s} +} + +// In reports whether p is in r. +func (p Point) In(r Rectangle) bool { + return r.Min.X <= p.X && p.X < r.Max.X && + r.Min.Y <= p.Y && p.Y < r.Max.Y +} + // Size returns r's width and height. func (r Rectangle) Size() Point { return Point{X: r.Dx(), Y: r.Dy()} @@ -95,11 +106,20 @@ func (r Rectangle) Intersect(s Rectangle) Rectangle { if r.Max.Y > s.Max.Y { r.Max.Y = s.Max.Y } + if r.Empty() { + return Rectangle{} + } return r } // Union returns the union of r and s. func (r Rectangle) Union(s Rectangle) Rectangle { + if r.Empty() { + return s + } + if s.Empty() { + return r + } if r.Min.X > s.Min.X { r.Min.X = s.Min.X } diff --git a/vendor/gioui.org/font/opentype/opentype.go b/vendor/gioui.org/font/opentype/opentype.go index 27afea4..1f42330 100644 --- a/vendor/gioui.org/font/opentype/opentype.go +++ b/vendor/gioui.org/font/opentype/opentype.go @@ -5,29 +5,32 @@ package opentype import ( + "bytes" "io" - "unicode" "unicode/utf8" + "golang.org/x/image/font" + "golang.org/x/image/font/sfnt" + "golang.org/x/image/math/fixed" + "gioui.org/f32" "gioui.org/op" "gioui.org/op/clip" "gioui.org/text" - "golang.org/x/image/font" - "golang.org/x/image/font/sfnt" - "golang.org/x/image/math/fixed" ) -// Font implements text.Face. +// Font implements text.Face. Its methods are safe to use +// concurrently. type Font struct { font *sfnt.Font - buf sfnt.Buffer } -// Collection is a collection of one or more fonts. +// Collection is a collection of one or more fonts. When used as a text.Face, +// each rune will be assigned a glyph from the first font in the collection +// that supports it. type Collection struct { - coll *sfnt.Collection + fonts []*opentype } type opentype struct { @@ -35,6 +38,13 @@ type opentype struct { Hinting font.Hinting } +// a glyph represents a rune and its advance according to a Font. +// TODO: remove this type and work on io.Readers directly. +type glyph struct { + Rune rune + Advance fixed.Int26_6 +} + // NewFont parses an SFNT font, such as TTF or OTF data, from a []byte // data source. func Parse(src []byte) (*Font, error) { @@ -55,7 +65,7 @@ func ParseCollection(src []byte) (*Collection, error) { if err != nil { return nil, err } - return &Collection{c}, nil + return newCollectionFrom(c) } // ParseCollectionReaderAt parses an SFNT collection, such as TTC or OTC data, @@ -68,21 +78,35 @@ func ParseCollectionReaderAt(src io.ReaderAt) (*Collection, error) { if err != nil { return nil, err } - return &Collection{c}, nil + return newCollectionFrom(c) +} + +func newCollectionFrom(coll *sfnt.Collection) (*Collection, error) { + fonts := make([]*opentype, coll.NumFonts()) + for i := range fonts { + fnt, err := coll.Font(i) + if err != nil { + return nil, err + } + fonts[i] = &opentype{ + Font: fnt, + Hinting: font.HintingFull, + } + } + return &Collection{fonts: fonts}, nil } // NumFonts returns the number of fonts in the collection. func (c *Collection) NumFonts() int { - return c.coll.NumFonts() + return len(c.fonts) } // Font returns the i'th font in the collection. func (c *Collection) Font(i int) (*Font, error) { - fnt, err := c.coll.Font(i) - if err != nil { - return nil, err + if i < 0 || len(c.fonts) <= i { + return nil, sfnt.ErrNotFound } - return &Font{font: fnt}, nil + return &Font{font: c.fonts[i].Font}, nil } func (f *Font) Layout(ppem fixed.Int26_6, maxWidth int, txt io.Reader) ([]text.Line, error) { @@ -90,31 +114,69 @@ func (f *Font) Layout(ppem fixed.Int26_6, maxWidth int, txt io.Reader) ([]text.L if err != nil { return nil, err } - return layoutText(&f.buf, ppem, maxWidth, &opentype{Font: f.font, Hinting: font.HintingFull}, glyphs) + fonts := []*opentype{{Font: f.font, Hinting: font.HintingFull}} + var buf sfnt.Buffer + return layoutText(&buf, ppem, maxWidth, fonts, glyphs) } -func (f *Font) Shape(ppem fixed.Int26_6, str []text.Glyph) op.CallOp { - return textPath(&f.buf, ppem, &opentype{Font: f.font, Hinting: font.HintingFull}, str) +func (f *Font) Shape(ppem fixed.Int26_6, str text.Layout) clip.PathSpec { + var buf sfnt.Buffer + return textPath(&buf, ppem, []*opentype{{Font: f.font, Hinting: font.HintingFull}}, str) } func (f *Font) Metrics(ppem fixed.Int26_6) font.Metrics { o := &opentype{Font: f.font, Hinting: font.HintingFull} - return o.Metrics(&f.buf, ppem) + var buf sfnt.Buffer + return o.Metrics(&buf, ppem) +} + +func (c *Collection) Layout(ppem fixed.Int26_6, maxWidth int, txt io.Reader) ([]text.Line, error) { + glyphs, err := readGlyphs(txt) + if err != nil { + return nil, err + } + var buf sfnt.Buffer + return layoutText(&buf, ppem, maxWidth, c.fonts, glyphs) +} + +func (c *Collection) Shape(ppem fixed.Int26_6, str text.Layout) clip.PathSpec { + var buf sfnt.Buffer + return textPath(&buf, ppem, c.fonts, str) +} + +func fontForGlyph(buf *sfnt.Buffer, fonts []*opentype, r rune) *opentype { + if len(fonts) < 1 { + return nil + } + for _, f := range fonts { + if f.HasGlyph(buf, r) { + return f + } + } + return fonts[0] // Use replacement character from the first font if necessary } -func layoutText(sbuf *sfnt.Buffer, ppem fixed.Int26_6, maxWidth int, f *opentype, glyphs []text.Glyph) ([]text.Line, error) { - m := f.Metrics(sbuf, ppem) - lineTmpl := text.Line{ - Ascent: m.Ascent, +func layoutText(sbuf *sfnt.Buffer, ppem fixed.Int26_6, maxWidth int, fonts []*opentype, glyphs []glyph) ([]text.Line, error) { + var lines []text.Line + var nextLine text.Line + updateBounds := func(f *opentype) { + m := f.Metrics(sbuf, ppem) + if m.Ascent > nextLine.Ascent { + nextLine.Ascent = m.Ascent + } // m.Height is equal to m.Ascent + m.Descent + linegap. // Compute the descent including the linegap. - Descent: m.Height - m.Ascent, - Bounds: f.Bounds(sbuf, ppem), + descent := m.Height - m.Ascent + if descent > nextLine.Descent { + nextLine.Descent = descent + } + b := f.Bounds(sbuf, ppem) + nextLine.Bounds = nextLine.Bounds.Union(b) } - var lines []text.Line maxDotX := fixed.I(maxWidth) type state struct { r rune + f *opentype adv fixed.Int26_6 x fixed.Int26_6 idx int @@ -123,26 +185,33 @@ func layoutText(sbuf *sfnt.Buffer, ppem fixed.Int26_6, maxWidth int, f *opentype } var prev, word state endLine := func() { - line := lineTmpl - line.Layout = glyphs[:prev.idx:prev.idx] - line.Len = prev.len - line.Width = prev.x + prev.adv - line.Bounds.Max.X += prev.x - lines = append(lines, line) + if prev.f == nil && len(fonts) > 0 { + prev.f = fonts[0] + } + updateBounds(prev.f) + nextLine.Layout = toLayout(glyphs[:prev.idx:prev.idx]) + nextLine.Width = prev.x + prev.adv + nextLine.Bounds.Max.X += prev.x + lines = append(lines, nextLine) glyphs = glyphs[prev.idx:] + nextLine = text.Line{} prev = state{} word = state{} } for prev.idx < len(glyphs) { g := &glyphs[prev.idx] - a, valid := f.GlyphAdvance(sbuf, ppem, g.Rune) next := state{ - r: g.Rune, - idx: prev.idx + 1, - len: prev.len + utf8.RuneLen(g.Rune), - x: prev.x + prev.adv, - adv: a, - valid: valid, + r: g.Rune, + f: fontForGlyph(sbuf, fonts, g.Rune), + idx: prev.idx + 1, + len: prev.len + utf8.RuneLen(g.Rune), + x: prev.x + prev.adv, + } + if next.f != nil { + if next.f != prev.f { + updateBounds(next.f) + } + next.adv, next.valid = next.f.GlyphAdvance(sbuf, ppem, g.Rune) } if g.Rune == '\n' { // The newline is zero width; use the previous @@ -153,8 +222,8 @@ func layoutText(sbuf *sfnt.Buffer, ppem fixed.Int26_6, maxWidth int, f *opentype continue } var k fixed.Int26_6 - if prev.valid { - k = f.Kern(sbuf, ppem, prev.r, next.r) + if prev.valid && next.f != nil { + k = next.f.Kern(sbuf, ppem, prev.r, next.r) } // Break the line if we're out of space. if prev.idx > 0 && next.x+next.adv+k > maxDotX { @@ -181,16 +250,30 @@ func layoutText(sbuf *sfnt.Buffer, ppem fixed.Int26_6, maxWidth int, f *opentype return lines, nil } -func textPath(buf *sfnt.Buffer, ppem fixed.Int26_6, f *opentype, str []text.Glyph) op.CallOp { +// toLayout converts a slice of glyphs to a text.Layout. +func toLayout(glyphs []glyph) text.Layout { + var buf bytes.Buffer + advs := make([]fixed.Int26_6, len(glyphs)) + for i, g := range glyphs { + buf.WriteRune(g.Rune) + advs[i] = glyphs[i].Advance + } + return text.Layout{Text: buf.String(), Advances: advs} +} + +func textPath(buf *sfnt.Buffer, ppem fixed.Int26_6, fonts []*opentype, str text.Layout) clip.PathSpec { var lastPos f32.Point var builder clip.Path - ops := new(op.Ops) - m := op.Record(ops) var x fixed.Int26_6 - builder.Begin(ops) - for _, g := range str { - if !unicode.IsSpace(g.Rune) { - segs, ok := f.LoadGlyph(buf, ppem, g.Rune) + builder.Begin(new(op.Ops)) + rune := 0 + for _, r := range str.Text { + if !unicode.IsSpace(r) { + f := fontForGlyph(buf, fonts, r) + if f == nil { + continue + } + segs, ok := f.LoadGlyph(buf, ppem, r) if !ok { continue } @@ -236,14 +319,14 @@ func textPath(buf *sfnt.Buffer, ppem fixed.Int26_6, f *opentype, str []text.Glyp } lastPos = lastPos.Add(lastArg) } - x += g.Advance + x += str.Advances[rune] + rune++ } - builder.End().Add(ops) - return m.Stop() + return builder.End() } -func readGlyphs(r io.Reader) ([]text.Glyph, error) { - var glyphs []text.Glyph +func readGlyphs(r io.Reader) ([]glyph, error) { + var glyphs []glyph buf := make([]byte, 0, 1024) for { n, err := r.Read(buf[len(buf):cap(buf)]) @@ -257,7 +340,7 @@ func readGlyphs(r io.Reader) ([]text.Glyph, error) { for i < lim { c, s := utf8.DecodeRune(buf[i:]) i += s - glyphs = append(glyphs, text.Glyph{Rune: c}) + glyphs = append(glyphs, glyph{Rune: c}) } n = copy(buf, buf[i:]) buf = buf[:n] @@ -271,6 +354,11 @@ func readGlyphs(r io.Reader) ([]text.Glyph, error) { return glyphs, nil } +func (f *opentype) HasGlyph(buf *sfnt.Buffer, r rune) bool { + g, err := f.Font.GlyphIndex(buf, r) + return g != 0 && err == nil +} + func (f *opentype) GlyphAdvance(buf *sfnt.Buffer, ppem fixed.Int26_6, r rune) (advance fixed.Int26_6, ok bool) { g, err := f.Font.GlyphIndex(buf, r) if err != nil { diff --git a/vendor/gioui.org/gesture/gesture.go b/vendor/gioui.org/gesture/gesture.go index 36ecd80..2d63963 100644 --- a/vendor/gioui.org/gesture/gesture.go +++ b/vendor/gioui.org/gesture/gesture.go @@ -10,6 +10,7 @@ and scrolling. package gesture import ( + "image" "math" "runtime" "time" @@ -26,11 +27,49 @@ import ( // The duration is somewhat arbitrary. const doubleClickDuration = 200 * time.Millisecond +// Hover detects the hover gesture for a pointer area. +type Hover struct { + // entered tracks whether the pointer is inside the gesture. + entered bool + // pid is the pointer.ID. + pid pointer.ID +} + +// Add the gesture to detect hovering over the current pointer area. +func (h *Hover) Add(ops *op.Ops) { + pointer.InputOp{ + Tag: h, + Types: pointer.Enter | pointer.Leave, + }.Add(ops) +} + +// Hovered returns whether a pointer is inside the area. +func (h *Hover) Hovered(q event.Queue) bool { + for _, ev := range q.Events(h) { + e, ok := ev.(pointer.Event) + if !ok { + continue + } + switch e.Type { + case pointer.Leave: + if h.entered && h.pid == e.PointerID { + h.entered = false + } + case pointer.Enter: + if !h.entered { + h.pid = e.PointerID + } + if h.pid == e.PointerID { + h.entered = true + } + } + } + return h.entered +} + // Click detects click gestures in the form // of ClickEvents. type Click struct { - // state tracks the gesture state. - state ClickState // clickedAt is the timestamp at which // the last click occurred. clickedAt time.Duration @@ -45,8 +84,6 @@ type Click struct { pid pointer.ID } -type ClickState uint8 - // ClickEvent represent a click action, either a // TypePress for the beginning of a click or a // TypeClick for a completed click. @@ -65,6 +102,7 @@ type ClickType uint8 // Drag detects drag gestures in the form of pointer.Drag events. type Drag struct { dragging bool + pressed bool pid pointer.ID start f32.Point grab bool @@ -92,6 +130,7 @@ type Axis uint8 const ( Horizontal Axis = iota Vertical + Both ) const ( @@ -109,7 +148,7 @@ const ( const ( // StateIdle is the default scroll state. StateIdle ScrollState = iota - // StateDrag is reported during drag gestures. + // StateDragging is reported during drag gestures. StateDragging // StateFlinging is reported when a fling is // in progress. @@ -120,14 +159,23 @@ var touchSlop = unit.Dp(3) // Add the handler to the operation list to receive click events. func (c *Click) Add(ops *op.Ops) { - op := pointer.InputOp{ + pointer.InputOp{ Tag: c, Types: pointer.Press | pointer.Release | pointer.Enter | pointer.Leave, - } - op.Add(ops) + }.Add(ops) +} + +// Hovered returns whether a pointer is inside the area. +func (c *Click) Hovered() bool { + return c.entered } -// Events returns the next click event, if any. +// Pressed returns whether a pointer is pressing. +func (c *Click) Pressed() bool { + return c.pressed +} + +// Events returns the next click events, if any. func (c *Click) Events(q event.Queue) []ClickEvent { var events []ClickEvent for _, evt := range q.Events(c) { @@ -163,7 +211,7 @@ func (c *Click) Events(q event.Queue) []ClickEvent { if c.pressed { break } - if e.Source == pointer.Mouse && e.Buttons != pointer.ButtonLeft { + if e.Source == pointer.Mouse && e.Buttons != pointer.ButtonPrimary { break } if !c.entered { @@ -193,12 +241,17 @@ func (c *Click) Events(q event.Queue) []ClickEvent { return events } +func (ClickEvent) ImplementsEvent() {} + // Add the handler to the operation list to receive scroll events. -func (s *Scroll) Add(ops *op.Ops) { +// The bounds variable refers to the scrolling boundaries +// as defined in io/pointer.InputOp. +func (s *Scroll) Add(ops *op.Ops, bounds image.Rectangle) { oph := pointer.InputOp{ - Tag: s, - Grab: s.grab, - Types: pointer.Press | pointer.Drag | pointer.Release | pointer.Scroll, + Tag: s, + Grab: s.grab, + Types: pointer.Press | pointer.Drag | pointer.Release | pointer.Scroll, + ScrollBounds: bounds, } oph.Add(ops) if s.flinger.Active() { @@ -254,9 +307,6 @@ func (s *Scroll) Scroll(cfg unit.Metric, q event.Queue, t time.Time, axis Axis) s.dragging = false s.grab = false case pointer.Scroll: - if e.Priority < pointer.Foremost { - continue - } switch s.axis { case Horizontal: s.scroll += e.Scroll.X @@ -311,12 +361,11 @@ func (s *Scroll) State() ScrollState { // Add the handler to the operation list to receive drag events. func (d *Drag) Add(ops *op.Ops) { - op := pointer.InputOp{ + pointer.InputOp{ Tag: d, Grab: d.grab, Types: pointer.Press | pointer.Drag | pointer.Release, - } - op.Add(ops) + }.Add(ops) } // Events returns the next drag events, if any. @@ -330,9 +379,10 @@ func (d *Drag) Events(cfg unit.Metric, q event.Queue, axis Axis) []pointer.Event switch e.Type { case pointer.Press: - if !(e.Buttons == pointer.ButtonLeft || e.Source == pointer.Touch) { + if !(e.Buttons == pointer.ButtonPrimary || e.Source == pointer.Touch) { continue } + d.pressed = true if d.dragging { continue } @@ -348,6 +398,8 @@ func (d *Drag) Events(cfg unit.Metric, q event.Queue, axis Axis) []pointer.Event e.Position.Y = d.start.Y case Vertical: e.Position.X = d.start.X + case Both: + // Do nothing } if e.Priority < pointer.Grabbed { diff := e.Position.Sub(d.start) @@ -357,6 +409,7 @@ func (d *Drag) Events(cfg unit.Metric, q event.Queue, axis Axis) []pointer.Event } } case pointer.Release, pointer.Cancel: + d.pressed = false if !d.dragging || e.PointerID != d.pid { continue } @@ -370,6 +423,12 @@ func (d *Drag) Events(cfg unit.Metric, q event.Queue, axis Axis) []pointer.Event return events } +// Dragging reports whether it is currently in use. +func (d *Drag) Dragging() bool { return d.dragging } + +// Pressed returns whether a pointer is pressing. +func (d *Drag) Pressed() bool { return d.pressed } + func (a Axis) String() string { switch a { case Horizontal: diff --git a/vendor/gioui.org/gpu/api.go b/vendor/gioui.org/gpu/api.go new file mode 100644 index 0000000..d347e5a --- /dev/null +++ b/vendor/gioui.org/gpu/api.go @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package gpu + +import "gioui.org/gpu/internal/driver" + +// An API carries the necessary GPU API specific resources to create a Device. +// There is an API type for each supported GPU API such as OpenGL and Direct3D. +type API = driver.API + +// A RenderTarget denotes the destination framebuffer for a frame. +type RenderTarget = driver.RenderTarget + +// OpenGLRenderTarget is a render target suitable for the OpenGL backend. +type OpenGLRenderTarget = driver.OpenGLRenderTarget + +// Direct3D11RenderTarget is a render target suitable for the Direct3D 11 backend. +type Direct3D11RenderTarget = driver.Direct3D11RenderTarget + +// MetalRenderTarget is a render target suitable for the Metal backend. +type MetalRenderTarget = driver.MetalRenderTarget + +// VulkanRenderTarget is a render target suitable for the Vulkan backend. +type VulkanRenderTarget = driver.VulkanRenderTarget + +// OpenGL denotes the OpenGL or OpenGL ES API. +type OpenGL = driver.OpenGL + +// Direct3D11 denotes the Direct3D API. +type Direct3D11 = driver.Direct3D11 + +// Metal denotes the Apple Metal API. +type Metal = driver.Metal + +// Vulkan denotes the Vulkan API. +type Vulkan = driver.Vulkan + +// ErrDeviceLost is returned from GPU operations when the underlying GPU device +// is lost and should be recreated. +var ErrDeviceLost = driver.ErrDeviceLost diff --git a/vendor/gioui.org/gpu/backend/backend.go b/vendor/gioui.org/gpu/backend/backend.go deleted file mode 100644 index 871eea4..0000000 --- a/vendor/gioui.org/gpu/backend/backend.go +++ /dev/null @@ -1,204 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package backend - -import ( - "image" - "time" -) - -// Device represents the abstraction of underlying GPU -// APIs such as OpenGL, Direct3D useful for rendering Gio -// operations. -type Device interface { - BeginFrame() - EndFrame() - Caps() Caps - NewTimer() Timer - // IsContinuousTime reports whether all timer measurements - // are valid at the point of call. - IsTimeContinuous() bool - NewTexture(format TextureFormat, width, height int, minFilter, magFilter TextureFilter, bindings BufferBinding) (Texture, error) - CurrentFramebuffer() Framebuffer - NewFramebuffer(tex Texture, depthBits int) (Framebuffer, error) - NewImmutableBuffer(typ BufferBinding, data []byte) (Buffer, error) - NewBuffer(typ BufferBinding, size int) (Buffer, error) - NewProgram(vertexShader, fragmentShader ShaderSources) (Program, error) - NewInputLayout(vertexShader ShaderSources, layout []InputDesc) (InputLayout, error) - - DepthFunc(f DepthFunc) - ClearDepth(d float32) - Clear(r, g, b, a float32) - Viewport(x, y, width, height int) - DrawArrays(mode DrawMode, off, count int) - DrawElements(mode DrawMode, off, count int) - SetBlend(enable bool) - SetDepthTest(enable bool) - DepthMask(mask bool) - BlendFunc(sfactor, dfactor BlendFactor) - - BindInputLayout(i InputLayout) - BindProgram(p Program) - BindFramebuffer(f Framebuffer) - BindTexture(unit int, t Texture) - BindVertexBuffer(b Buffer, stride, offset int) - BindIndexBuffer(b Buffer) -} - -type ShaderSources struct { - GLSL100ES string - GLSL300ES string - GLSL130 string - GLSL150 string - HLSL []byte - Uniforms UniformsReflection - Inputs []InputLocation - Textures []TextureBinding -} - -type UniformsReflection struct { - Blocks []UniformBlock - Locations []UniformLocation - Size int -} - -type TextureBinding struct { - Name string - Binding int -} - -type UniformBlock struct { - Name string - Binding int -} - -type UniformLocation struct { - Name string - Type DataType - Size int - Offset int -} - -type InputLocation struct { - // For GLSL. - Name string - Location int - // For HLSL. - Semantic string - SemanticIndex int - - Type DataType - Size int -} - -// InputDesc describes a vertex attribute as laid out in a Buffer. -type InputDesc struct { - Type DataType - Size int - - Offset int -} - -// InputLayout is the backend specific representation of the mapping -// between Buffers and shader attributes. -type InputLayout interface { - Release() -} - -type BlendFactor uint8 - -type DrawMode uint8 - -type TextureFilter uint8 -type TextureFormat uint8 - -type BufferBinding uint8 - -type DataType uint8 - -type DepthFunc uint8 - -type Features uint - -type Caps struct { - Features Features - MaxTextureSize int -} - -type Program interface { - Release() - SetVertexUniforms(buf Buffer) - SetFragmentUniforms(buf Buffer) -} - -type Buffer interface { - Release() - Upload(data []byte) -} - -type Framebuffer interface { - Invalidate() - Release() - ReadPixels(src image.Rectangle, pixels []byte) error -} - -type Timer interface { - Begin() - End() - Duration() (time.Duration, bool) - Release() -} - -type Texture interface { - Upload(img *image.RGBA) - Release() -} - -const ( - DepthFuncGreater DepthFunc = iota - DepthFuncGreaterEqual -) - -const ( - DataTypeFloat DataType = iota - DataTypeInt - DataTypeShort -) - -const ( - BufferBindingIndices BufferBinding = 1 << iota - BufferBindingVertices - BufferBindingUniforms - BufferBindingTexture - BufferBindingFramebuffer -) - -const ( - TextureFormatSRGB TextureFormat = iota - TextureFormatFloat -) - -const ( - FilterNearest TextureFilter = iota - FilterLinear -) - -const ( - FeatureTimers Features = iota -) - -const ( - DrawModeTriangleStrip DrawMode = iota - DrawModeTriangles -) - -const ( - BlendFactorOne BlendFactor = iota - BlendFactorOneMinusSrcAlpha - BlendFactorZero - BlendFactorDstColor -) - -func (f Features) Has(feats Features) bool { - return f&feats == feats -} diff --git a/vendor/gioui.org/gpu/caches.go b/vendor/gioui.org/gpu/caches.go index cefba83..c6a71bc 100644 --- a/vendor/gioui.org/gpu/caches.go +++ b/vendor/gioui.org/gpu/caches.go @@ -6,7 +6,6 @@ import ( "fmt" "gioui.org/f32" - "gioui.org/internal/ops" ) type resourceCache struct { @@ -19,17 +18,18 @@ type resourceCache struct { // since benchmarking showed them as a bottleneck. type opCache struct { // store the index + 1 in cache this key is stored in - index map[ops.Key]int + index map[opKey]int // list of indexes in cache that are free and can be used freelist []int cache []opCacheValue } type opCacheValue struct { - data pathData + data pathData + bounds f32.Rectangle // the fields below are handled by opCache - key ops.Key + key opKey keep bool } @@ -70,7 +70,8 @@ func (r *resourceCache) frame() { } func (r *resourceCache) release() { - for _, v := range r.newRes { + r.frame() + for _, v := range r.res { v.release() } r.newRes = nil @@ -79,13 +80,13 @@ func (r *resourceCache) release() { func newOpCache() *opCache { return &opCache{ - index: make(map[ops.Key]int), + index: make(map[opKey]int), freelist: make([]int, 0), cache: make([]opCacheValue, 0), } } -func (r *opCache) get(key ops.Key) (o opCacheValue, exist bool) { +func (r *opCache) get(key opKey) (o opCacheValue, exist bool) { v := r.index[key] if v == 0 { return @@ -94,7 +95,7 @@ func (r *opCache) get(key ops.Key) (o opCacheValue, exist bool) { return r.cache[v-1], true } -func (r *opCache) put(key ops.Key, val opCacheValue) { +func (r *opCache) put(key opKey, val opCacheValue) { v := r.index[key] val.keep = true val.key = key diff --git a/vendor/gioui.org/gpu/clip.go b/vendor/gioui.org/gpu/clip.go index 51022a0..8ae25b5 100644 --- a/vendor/gioui.org/gpu/clip.go +++ b/vendor/gioui.org/gpu/clip.go @@ -2,11 +2,10 @@ package gpu import ( "gioui.org/f32" - "gioui.org/internal/ops" + "gioui.org/internal/stroke" ) type quadSplitter struct { - verts []byte bounds f32.Rectangle contour uint32 d *drawOps @@ -48,7 +47,7 @@ func (qs *quadSplitter) encodeQuadTo(from, ctrl, to f32.Point) { encodeQuadTo(data, qs.contour, from, ctrl, to) } -func (qs *quadSplitter) splitAndEncode(quad ops.Quad) { +func (qs *quadSplitter) splitAndEncode(quad stroke.QuadSegment) { cbnd := f32.Rectangle{ Min: quad.From, Max: quad.To, @@ -94,5 +93,22 @@ func (qs *quadSplitter) splitAndEncode(quad ops.Quad) { } } - qs.bounds = qs.bounds.Union(cbnd) + qs.bounds = unionRect(qs.bounds, cbnd) +} + +// Union is like f32.Rectangle.Union but ignores empty rectangles. +func unionRect(r, s f32.Rectangle) f32.Rectangle { + if r.Min.X > s.Min.X { + r.Min.X = s.Min.X + } + if r.Min.Y > s.Min.Y { + r.Min.Y = s.Min.Y + } + if r.Max.X < s.Max.X { + r.Max.X = s.Max.X + } + if r.Max.Y < s.Max.Y { + r.Max.Y = s.Max.Y + } + return r } diff --git a/vendor/gioui.org/gpu/compute.go b/vendor/gioui.org/gpu/compute.go new file mode 100644 index 0000000..625658c --- /dev/null +++ b/vendor/gioui.org/gpu/compute.go @@ -0,0 +1,2219 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package gpu + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "hash/maphash" + "image" + "image/color" + "image/draw" + "image/png" + "io/ioutil" + "math" + "math/bits" + "runtime" + "sort" + "time" + "unsafe" + + "gioui.org/cpu" + "gioui.org/f32" + "gioui.org/gpu/internal/driver" + "gioui.org/internal/byteslice" + "gioui.org/internal/f32color" + "gioui.org/internal/ops" + "gioui.org/internal/scene" + "gioui.org/layout" + "gioui.org/op" + "gioui.org/shader" + "gioui.org/shader/gio" + "gioui.org/shader/piet" +) + +type compute struct { + ctx driver.Device + + collector collector + enc encoder + texOps []textureOp + viewport image.Point + maxTextureDim int + srgb bool + atlases []*textureAtlas + frameCount uint + moves []atlasMove + + programs struct { + elements computeProgram + tileAlloc computeProgram + pathCoarse computeProgram + backdrop computeProgram + binning computeProgram + coarse computeProgram + kernel4 computeProgram + } + buffers struct { + config sizedBuffer + scene sizedBuffer + state sizedBuffer + memory sizedBuffer + } + output struct { + blitPipeline driver.Pipeline + + buffer sizedBuffer + + uniforms *copyUniforms + uniBuf driver.Buffer + + layerVertices []layerVertex + descriptors *piet.Kernel4DescriptorSetLayout + + nullMaterials driver.Texture + } + // imgAllocs maps imageOpData.handles to allocs. + imgAllocs map[interface{}]*atlasAlloc + // materials contains the pre-processed materials (transformed images for + // now, gradients etc. later) packed in a texture atlas. The atlas is used + // as source in kernel4. + materials struct { + // allocs maps texture ops the their atlases and FillImage offsets. + allocs map[textureKey]materialAlloc + + pipeline driver.Pipeline + buffer sizedBuffer + quads []materialVertex + uniforms struct { + u *materialUniforms + buf driver.Buffer + } + } + timers struct { + profile string + t *timers + compact *timer + render *timer + blit *timer + } + + // CPU fallback fields. + useCPU bool + dispatcher *dispatcher + + // The following fields hold scratch space to avoid garbage. + zeroSlice []byte + memHeader *memoryHeader + conf *config +} + +type materialAlloc struct { + alloc *atlasAlloc + offset image.Point +} + +type layer struct { + rect image.Rectangle + alloc *atlasAlloc + ops []paintOp + materials *textureAtlas +} + +type allocQuery struct { + atlas *textureAtlas + size image.Point + empty bool + format driver.TextureFormat + bindings driver.BufferBinding + nocompact bool +} + +type atlasAlloc struct { + atlas *textureAtlas + rect image.Rectangle + cpu bool + dead bool + frameCount uint +} + +type atlasMove struct { + src *textureAtlas + dstPos image.Point + srcRect image.Rectangle + cpu bool +} + +type textureAtlas struct { + image driver.Texture + format driver.TextureFormat + bindings driver.BufferBinding + hasCPU bool + cpuImage cpu.ImageDescriptor + size image.Point + allocs []*atlasAlloc + packer packer + realized bool + lastFrame uint + compact bool +} + +type copyUniforms struct { + scale [2]float32 + pos [2]float32 + uvScale [2]float32 + _ [8]byte // Pad to 16 bytes. +} + +type materialUniforms struct { + scale [2]float32 + pos [2]float32 + emulatesRGB float32 + _ [12]byte // Pad to 16 bytes +} + +type collector struct { + hasher maphash.Hash + profile bool + reader ops.Reader + states []f32.Affine2D + clear bool + clearColor f32color.RGBA + clipStates []clipState + order []hashIndex + transStack []transEntry + prevFrame opsCollector + frame opsCollector +} + +type transEntry struct { + t f32.Affine2D + relTrans f32.Affine2D +} + +type hashIndex struct { + index int + hash uint64 +} + +type opsCollector struct { + paths []byte + clipCmds []clipCmd + ops []paintOp + layers []layer +} + +type paintOp struct { + clipStack []clipCmd + offset image.Point + state paintKey + intersect f32.Rectangle + hash uint64 + layer int + texOpIdx int +} + +// clipCmd describes a clipping command ready to be used for the compute +// pipeline. +type clipCmd struct { + // union of the bounds of the operations that are clipped. + union f32.Rectangle + state clipKey + path []byte + pathKey ops.Key + absBounds f32.Rectangle +} + +type encoderState struct { + relTrans f32.Affine2D + clip *clipState + + paintKey +} + +// clipKey completely describes a clip operation (along with its path) and is appropriate +// for hashing and equality checks. +type clipKey struct { + bounds f32.Rectangle + strokeWidth float32 + relTrans f32.Affine2D + pathHash uint64 +} + +// paintKey completely defines a paint operation. It is suitable for hashing and +// equality checks. +type paintKey struct { + t f32.Affine2D + matType materialType + // Current paint.ImageOp + image imageOpData + // Current paint.ColorOp, if any. + color color.NRGBA + + // Current paint.LinearGradientOp. + stop1 f32.Point + stop2 f32.Point + color1 color.NRGBA + color2 color.NRGBA +} + +type clipState struct { + absBounds f32.Rectangle + parent *clipState + path []byte + pathKey ops.Key + intersect f32.Rectangle + push bool + + clipKey +} + +type layerVertex struct { + posX, posY float32 + u, v float32 +} + +// materialVertex describes a vertex of a quad used to render a transformed +// material. +type materialVertex struct { + posX, posY float32 + u, v float32 +} + +// textureKey identifies textureOp. +type textureKey struct { + handle interface{} + transform f32.Affine2D + bounds image.Rectangle +} + +// textureOp represents an paintOp that requires texture space. +type textureOp struct { + img imageOpData + key textureKey + // offset is the integer offset separated from key.transform to increase cache hit rate. + off image.Point + // matAlloc is the atlas placement for material. + matAlloc materialAlloc + // imgAlloc is the atlas placement for the source image + imgAlloc *atlasAlloc +} + +type encoder struct { + scene []scene.Command + npath int + npathseg int + ntrans int +} + +type encodeState struct { + trans f32.Affine2D + clip f32.Rectangle +} + +// sizedBuffer holds a GPU buffer, or its equivalent CPU memory. +type sizedBuffer struct { + size int + buffer driver.Buffer + // cpuBuf is initialized when useCPU is true. + cpuBuf cpu.BufferDescriptor +} + +// computeProgram holds a compute program, or its equivalent CPU implementation. +type computeProgram struct { + prog driver.Program + + // CPU fields. + progInfo *cpu.ProgramInfo + descriptors unsafe.Pointer + buffers []*cpu.BufferDescriptor +} + +// config matches Config in setup.h +type config struct { + n_elements uint32 // paths + n_pathseg uint32 + width_in_tiles uint32 + height_in_tiles uint32 + tile_alloc memAlloc + bin_alloc memAlloc + ptcl_alloc memAlloc + pathseg_alloc memAlloc + anno_alloc memAlloc + trans_alloc memAlloc +} + +// memAlloc matches Alloc in mem.h +type memAlloc struct { + offset uint32 + //size uint32 +} + +// memoryHeader matches the header of Memory in mem.h. +type memoryHeader struct { + mem_offset uint32 + mem_error uint32 +} + +// rect is a oriented rectangle. +type rectangle [4]f32.Point + +const ( + layersBindings = driver.BufferBindingShaderStorageWrite | driver.BufferBindingTexture + materialsBindings = driver.BufferBindingFramebuffer | driver.BufferBindingShaderStorageRead + // Materials and layers can share texture storage if their bindings match. + combinedBindings = layersBindings | materialsBindings +) + +// GPU structure sizes and constants. +const ( + tileWidthPx = 32 + tileHeightPx = 32 + ptclInitialAlloc = 1024 + kernel4OutputUnit = 2 + kernel4AtlasUnit = 3 + + pathSize = 12 + binSize = 8 + pathsegSize = 52 + annoSize = 32 + transSize = 24 + stateSize = 60 + stateStride = 4 + 2*stateSize +) + +// mem.h constants. +const ( + memNoError = 0 // NO_ERROR + memMallocFailed = 1 // ERR_MALLOC_FAILED +) + +func newCompute(ctx driver.Device) (*compute, error) { + caps := ctx.Caps() + maxDim := caps.MaxTextureSize + // Large atlas textures cause artifacts due to precision loss in + // shaders. + if cap := 8192; maxDim > cap { + maxDim = cap + } + // The compute programs can only span 128x64 tiles. Limit to 64 for now, and leave the + // complexity of a rectangular limit for later. + if computeCap := 4096; maxDim > computeCap { + maxDim = computeCap + } + g := &compute{ + ctx: ctx, + maxTextureDim: maxDim, + srgb: caps.Features.Has(driver.FeatureSRGB), + conf: new(config), + memHeader: new(memoryHeader), + } + null, err := ctx.NewTexture(driver.TextureFormatRGBA8, 1, 1, driver.FilterNearest, driver.FilterNearest, driver.BufferBindingShaderStorageRead) + if err != nil { + g.Release() + return nil, err + } + g.output.nullMaterials = null + shaders := []struct { + prog *computeProgram + src shader.Sources + info *cpu.ProgramInfo + }{ + {&g.programs.elements, piet.Shader_elements_comp, piet.ElementsProgramInfo}, + {&g.programs.tileAlloc, piet.Shader_tile_alloc_comp, piet.Tile_allocProgramInfo}, + {&g.programs.pathCoarse, piet.Shader_path_coarse_comp, piet.Path_coarseProgramInfo}, + {&g.programs.backdrop, piet.Shader_backdrop_comp, piet.BackdropProgramInfo}, + {&g.programs.binning, piet.Shader_binning_comp, piet.BinningProgramInfo}, + {&g.programs.coarse, piet.Shader_coarse_comp, piet.CoarseProgramInfo}, + {&g.programs.kernel4, piet.Shader_kernel4_comp, piet.Kernel4ProgramInfo}, + } + if !caps.Features.Has(driver.FeatureCompute) { + if !cpu.Supported { + return nil, errors.New("gpu: missing support for compute programs") + } + g.useCPU = true + } + if g.useCPU { + g.dispatcher = newDispatcher(runtime.NumCPU()) + } + + copyVert, copyFrag, err := newShaders(ctx, gio.Shader_copy_vert, gio.Shader_copy_frag) + if err != nil { + g.Release() + return nil, err + } + defer copyVert.Release() + defer copyFrag.Release() + pipe, err := ctx.NewPipeline(driver.PipelineDesc{ + VertexShader: copyVert, + FragmentShader: copyFrag, + VertexLayout: driver.VertexLayout{ + Inputs: []driver.InputDesc{ + {Type: shader.DataTypeFloat, Size: 2, Offset: 0}, + {Type: shader.DataTypeFloat, Size: 2, Offset: 4 * 2}, + }, + Stride: int(unsafe.Sizeof(g.output.layerVertices[0])), + }, + PixelFormat: driver.TextureFormatOutput, + BlendDesc: driver.BlendDesc{ + Enable: true, + SrcFactor: driver.BlendFactorOne, + DstFactor: driver.BlendFactorOneMinusSrcAlpha, + }, + Topology: driver.TopologyTriangles, + }) + if err != nil { + g.Release() + return nil, err + } + g.output.blitPipeline = pipe + g.output.uniforms = new(copyUniforms) + + buf, err := ctx.NewBuffer(driver.BufferBindingUniforms, int(unsafe.Sizeof(*g.output.uniforms))) + if err != nil { + g.Release() + return nil, err + } + g.output.uniBuf = buf + + materialVert, materialFrag, err := newShaders(ctx, gio.Shader_material_vert, gio.Shader_material_frag) + if err != nil { + g.Release() + return nil, err + } + defer materialVert.Release() + defer materialFrag.Release() + pipe, err = ctx.NewPipeline(driver.PipelineDesc{ + VertexShader: materialVert, + FragmentShader: materialFrag, + VertexLayout: driver.VertexLayout{ + Inputs: []driver.InputDesc{ + {Type: shader.DataTypeFloat, Size: 2, Offset: 0}, + {Type: shader.DataTypeFloat, Size: 2, Offset: 4 * 2}, + }, + Stride: int(unsafe.Sizeof(g.materials.quads[0])), + }, + PixelFormat: driver.TextureFormatRGBA8, + Topology: driver.TopologyTriangles, + }) + if err != nil { + g.Release() + return nil, err + } + g.materials.pipeline = pipe + g.materials.uniforms.u = new(materialUniforms) + + buf, err = ctx.NewBuffer(driver.BufferBindingUniforms, int(unsafe.Sizeof(*g.materials.uniforms.u))) + if err != nil { + g.Release() + return nil, err + } + g.materials.uniforms.buf = buf + + for _, shader := range shaders { + if !g.useCPU { + p, err := ctx.NewComputeProgram(shader.src) + if err != nil { + g.Release() + return nil, err + } + shader.prog.prog = p + } else { + shader.prog.progInfo = shader.info + } + } + if g.useCPU { + { + desc := new(piet.ElementsDescriptorSetLayout) + g.programs.elements.descriptors = unsafe.Pointer(desc) + g.programs.elements.buffers = []*cpu.BufferDescriptor{desc.Binding0(), desc.Binding1(), desc.Binding2(), desc.Binding3()} + } + { + desc := new(piet.Tile_allocDescriptorSetLayout) + g.programs.tileAlloc.descriptors = unsafe.Pointer(desc) + g.programs.tileAlloc.buffers = []*cpu.BufferDescriptor{desc.Binding0(), desc.Binding1()} + } + { + desc := new(piet.Path_coarseDescriptorSetLayout) + g.programs.pathCoarse.descriptors = unsafe.Pointer(desc) + g.programs.pathCoarse.buffers = []*cpu.BufferDescriptor{desc.Binding0(), desc.Binding1()} + } + { + desc := new(piet.BackdropDescriptorSetLayout) + g.programs.backdrop.descriptors = unsafe.Pointer(desc) + g.programs.backdrop.buffers = []*cpu.BufferDescriptor{desc.Binding0(), desc.Binding1()} + } + { + desc := new(piet.BinningDescriptorSetLayout) + g.programs.binning.descriptors = unsafe.Pointer(desc) + g.programs.binning.buffers = []*cpu.BufferDescriptor{desc.Binding0(), desc.Binding1()} + } + { + desc := new(piet.CoarseDescriptorSetLayout) + g.programs.coarse.descriptors = unsafe.Pointer(desc) + g.programs.coarse.buffers = []*cpu.BufferDescriptor{desc.Binding0(), desc.Binding1()} + } + { + desc := new(piet.Kernel4DescriptorSetLayout) + g.programs.kernel4.descriptors = unsafe.Pointer(desc) + g.programs.kernel4.buffers = []*cpu.BufferDescriptor{desc.Binding0(), desc.Binding1()} + g.output.descriptors = desc + } + } + return g, nil +} + +func newShaders(ctx driver.Device, vsrc, fsrc shader.Sources) (vert driver.VertexShader, frag driver.FragmentShader, err error) { + vert, err = ctx.NewVertexShader(vsrc) + if err != nil { + return + } + frag, err = ctx.NewFragmentShader(fsrc) + if err != nil { + vert.Release() + } + return +} + +func (g *compute) Frame(frameOps *op.Ops, target RenderTarget, viewport image.Point) error { + g.frameCount++ + g.collect(viewport, frameOps) + return g.frame(target) +} + +func (g *compute) collect(viewport image.Point, ops *op.Ops) { + g.viewport = viewport + g.collector.reset() + + g.texOps = g.texOps[:0] + g.collector.collect(ops, viewport, &g.texOps) +} + +func (g *compute) Clear(col color.NRGBA) { + g.collector.clear = true + g.collector.clearColor = f32color.LinearFromSRGB(col) +} + +func (g *compute) frame(target RenderTarget) error { + viewport := g.viewport + defFBO := g.ctx.BeginFrame(target, g.collector.clear, viewport) + defer g.ctx.EndFrame() + + t := &g.timers + if g.collector.profile && t.t == nil && g.ctx.Caps().Features.Has(driver.FeatureTimers) { + t.t = newTimers(g.ctx) + t.compact = t.t.newTimer() + t.render = t.t.newTimer() + t.blit = t.t.newTimer() + } + + if err := g.uploadImages(); err != nil { + return err + } + if err := g.renderMaterials(); err != nil { + return err + } + g.layer(viewport, g.texOps) + t.render.begin() + if err := g.renderLayers(viewport); err != nil { + return err + } + t.render.end() + d := driver.LoadDesc{ + ClearColor: g.collector.clearColor, + } + if g.collector.clear { + g.collector.clear = false + d.Action = driver.LoadActionClear + } + t.blit.begin() + g.blitLayers(d, defFBO, viewport) + t.blit.end() + t.compact.begin() + if err := g.compactAllocs(); err != nil { + return err + } + t.compact.end() + if g.collector.profile && t.t.ready() { + com, ren, blit := t.compact.Elapsed, t.render.Elapsed, t.blit.Elapsed + ft := com + ren + blit + q := 100 * time.Microsecond + ft = ft.Round(q) + com, ren, blit = com.Round(q), ren.Round(q), blit.Round(q) + t.profile = fmt.Sprintf("ft:%7s com: %7s ren:%7s blit:%7s", ft, com, ren, blit) + } + return nil +} + +func (g *compute) dumpAtlases() { + for i, a := range g.atlases { + dump := image.NewRGBA(image.Rectangle{Max: a.size}) + err := driver.DownloadImage(g.ctx, a.image, dump) + if err != nil { + panic(err) + } + nrgba := image.NewNRGBA(dump.Bounds()) + draw.Draw(nrgba, image.Rectangle{}, dump, image.Point{}, draw.Src) + var buf bytes.Buffer + if err := png.Encode(&buf, nrgba); err != nil { + panic(err) + } + if err := ioutil.WriteFile(fmt.Sprintf("dump-%d.png", i), buf.Bytes(), 0600); err != nil { + panic(err) + } + } +} + +func (g *compute) Profile() string { + return g.timers.profile +} + +func (g *compute) compactAllocs() error { + const ( + maxAllocAge = 3 + maxAtlasAge = 10 + ) + atlases := g.atlases + for _, a := range atlases { + if len(a.allocs) > 0 && g.frameCount-a.lastFrame > maxAtlasAge { + a.compact = true + } + } + for len(atlases) > 0 { + var ( + dstAtlas *textureAtlas + format driver.TextureFormat + bindings driver.BufferBinding + ) + g.moves = g.moves[:0] + addedLayers := false + useCPU := false + fill: + for len(atlases) > 0 { + srcAtlas := atlases[0] + allocs := srcAtlas.allocs + if !srcAtlas.compact { + atlases = atlases[1:] + continue + } + if addedLayers && (format != srcAtlas.format || srcAtlas.bindings&bindings != srcAtlas.bindings) { + break + } + format = srcAtlas.format + bindings = srcAtlas.bindings + for len(srcAtlas.allocs) > 0 { + a := srcAtlas.allocs[0] + n := len(srcAtlas.allocs) + if g.frameCount-a.frameCount > maxAllocAge { + a.dead = true + srcAtlas.allocs[0] = srcAtlas.allocs[n-1] + srcAtlas.allocs = srcAtlas.allocs[:n-1] + continue + } + size := a.rect.Size() + alloc, fits := g.atlasAlloc(allocQuery{ + atlas: dstAtlas, + size: size, + format: format, + bindings: bindings, + nocompact: true, + }) + if !fits { + break fill + } + dstAtlas = alloc.atlas + allocs = append(allocs, a) + addedLayers = true + useCPU = useCPU || a.cpu + dstAtlas.allocs = append(dstAtlas.allocs, a) + pos := alloc.rect.Min + g.moves = append(g.moves, atlasMove{ + src: srcAtlas, dstPos: pos, srcRect: a.rect, cpu: a.cpu, + }) + a.atlas = dstAtlas + a.rect = image.Rectangle{Min: pos, Max: pos.Add(a.rect.Size())} + srcAtlas.allocs[0] = srcAtlas.allocs[n-1] + srcAtlas.allocs = srcAtlas.allocs[:n-1] + } + srcAtlas.compact = false + srcAtlas.realized = false + srcAtlas.packer.clear() + srcAtlas.packer.newPage() + srcAtlas.packer.maxDims = image.Pt(g.maxTextureDim, g.maxTextureDim) + atlases = atlases[1:] + } + if !addedLayers { + break + } + outputSize := dstAtlas.packer.sizes[0] + if err := g.realizeAtlas(dstAtlas, useCPU, outputSize); err != nil { + return err + } + for _, move := range g.moves { + if !move.cpu { + g.ctx.CopyTexture(dstAtlas.image, move.dstPos, move.src.image, move.srcRect) + } else { + src := move.src.cpuImage.Data() + dst := dstAtlas.cpuImage.Data() + sstride := move.src.size.X * 4 + dstride := dstAtlas.size.X * 4 + copyImage(dst, dstride, move.dstPos, src, sstride, move.srcRect) + } + } + } + for i := len(g.atlases) - 1; i >= 0; i-- { + a := g.atlases[i] + if len(a.allocs) == 0 && g.frameCount-a.lastFrame > maxAtlasAge { + a.Release() + n := len(g.atlases) + g.atlases[i] = g.atlases[n-1] + g.atlases = g.atlases[:n-1] + } + } + return nil +} + +func copyImage(dst []byte, dstStride int, dstPos image.Point, src []byte, srcStride int, srcRect image.Rectangle) { + sz := srcRect.Size() + soff := srcRect.Min.Y*srcStride + srcRect.Min.X*4 + doff := dstPos.Y*dstStride + dstPos.X*4 + rowLen := sz.X * 4 + for y := 0; y < sz.Y; y++ { + srow := src[soff : soff+rowLen] + drow := dst[doff : doff+rowLen] + copy(drow, srow) + soff += srcStride + doff += dstStride + } +} + +func (g *compute) renderLayers(viewport image.Point) error { + layers := g.collector.frame.layers + for len(layers) > 0 { + var materials, dst *textureAtlas + addedLayers := false + g.enc.reset() + for len(layers) > 0 { + l := &layers[0] + if l.alloc != nil { + layers = layers[1:] + continue + } + if materials != nil { + if l.materials != nil && materials != l.materials { + // Only one materials texture per compute pass. + break + } + } else { + materials = l.materials + } + size := l.rect.Size() + alloc, fits := g.atlasAlloc(allocQuery{ + atlas: dst, + empty: true, + format: driver.TextureFormatRGBA8, + bindings: combinedBindings, + // Pad to avoid overlap. + size: size.Add(image.Pt(1, 1)), + }) + if !fits { + // Only one output atlas per compute pass. + break + } + dst = alloc.atlas + dst.compact = true + addedLayers = true + l.alloc = &alloc + dst.allocs = append(dst.allocs, l.alloc) + encodeLayer(*l, alloc.rect.Min, viewport, &g.enc, g.texOps) + layers = layers[1:] + } + if !addedLayers { + break + } + outputSize := dst.packer.sizes[0] + tileDims := image.Point{ + X: (outputSize.X + tileWidthPx - 1) / tileWidthPx, + Y: (outputSize.Y + tileHeightPx - 1) / tileHeightPx, + } + w, h := tileDims.X*tileWidthPx, tileDims.Y*tileHeightPx + if err := g.realizeAtlas(dst, g.useCPU, image.Pt(w, h)); err != nil { + return err + } + if err := g.render(materials, dst.image, dst.cpuImage, tileDims, dst.size.X*4); err != nil { + return err + } + } + return nil +} + +func (g *compute) blitLayers(d driver.LoadDesc, fbo driver.Texture, viewport image.Point) { + layers := g.collector.frame.layers + g.output.layerVertices = g.output.layerVertices[:0] + for _, l := range layers { + placef := layout.FPt(l.alloc.rect.Min) + sizef := layout.FPt(l.rect.Size()) + r := layout.FRect(l.rect) + quad := [4]layerVertex{ + {posX: float32(r.Min.X), posY: float32(r.Min.Y), u: placef.X, v: placef.Y}, + {posX: float32(r.Max.X), posY: float32(r.Min.Y), u: placef.X + sizef.X, v: placef.Y}, + {posX: float32(r.Max.X), posY: float32(r.Max.Y), u: placef.X + sizef.X, v: placef.Y + sizef.Y}, + {posX: float32(r.Min.X), posY: float32(r.Max.Y), u: placef.X, v: placef.Y + sizef.Y}, + } + g.output.layerVertices = append(g.output.layerVertices, quad[0], quad[1], quad[3], quad[3], quad[2], quad[1]) + g.ctx.PrepareTexture(l.alloc.atlas.image) + } + if len(g.output.layerVertices) > 0 { + vertexData := byteslice.Slice(g.output.layerVertices) + g.output.buffer.ensureCapacity(false, g.ctx, driver.BufferBindingVertices, len(vertexData)) + g.output.buffer.buffer.Upload(vertexData) + } + g.ctx.BeginRenderPass(fbo, d) + defer g.ctx.EndRenderPass() + if len(layers) == 0 { + return + } + g.ctx.Viewport(0, 0, viewport.X, viewport.Y) + g.ctx.BindPipeline(g.output.blitPipeline) + g.ctx.BindVertexBuffer(g.output.buffer.buffer, 0) + start := 0 + for len(layers) > 0 { + count := 0 + atlas := layers[0].alloc.atlas + for len(layers) > 0 { + l := layers[0] + if l.alloc.atlas != atlas { + break + } + layers = layers[1:] + const verticesPerQuad = 6 + count += verticesPerQuad + } + + // Transform positions to clip space: [-1, -1] - [1, 1], and texture + // coordinates to texture space: [0, 0] - [1, 1]. + clip := f32.Affine2D{}.Scale(f32.Pt(0, 0), f32.Pt(2/float32(viewport.X), 2/float32(viewport.Y))).Offset(f32.Pt(-1, -1)) + sx, _, ox, _, sy, oy := clip.Elems() + g.output.uniforms.scale = [2]float32{sx, sy} + g.output.uniforms.pos = [2]float32{ox, oy} + g.output.uniforms.uvScale = [2]float32{1 / float32(atlas.size.X), 1 / float32(atlas.size.Y)} + g.output.uniBuf.Upload(byteslice.Struct(g.output.uniforms)) + g.ctx.BindUniforms(g.output.uniBuf) + g.ctx.BindTexture(0, atlas.image) + g.ctx.DrawArrays(start, count) + start += count + } +} + +func (g *compute) renderMaterials() error { + m := &g.materials + for k, place := range m.allocs { + if place.alloc.dead { + delete(m.allocs, k) + } + } + texOps := g.texOps + for len(texOps) > 0 { + m.quads = m.quads[:0] + var ( + atlas *textureAtlas + imgAtlas *textureAtlas + ) + // A material is clipped to avoid drawing outside its atlas bounds. + // However, imprecision in the clipping may cause a single pixel + // overflow. + var padding = image.Pt(1, 1) + var allocStart int + for len(texOps) > 0 { + op := &texOps[0] + if a, exists := m.allocs[op.key]; exists { + g.touchAlloc(a.alloc) + op.matAlloc = a + texOps = texOps[1:] + continue + } + + if imgAtlas != nil && op.imgAlloc.atlas != imgAtlas { + // Only one image atlas per render pass. + break + } + imgAtlas = op.imgAlloc.atlas + quad := g.materialQuad(imgAtlas.size, op.key.transform, op.img, op.imgAlloc.rect.Min) + boundsf := quadBounds(quad) + bounds := boundRectF(boundsf) + bounds = bounds.Intersect(op.key.bounds) + + size := bounds.Size() + alloc, fits := g.atlasAlloc(allocQuery{ + atlas: atlas, + size: size.Add(padding), + format: driver.TextureFormatRGBA8, + bindings: combinedBindings, + }) + if !fits { + break + } + if atlas == nil { + allocStart = len(alloc.atlas.allocs) + } + atlas = alloc.atlas + alloc.cpu = g.useCPU + offsetf := layout.FPt(bounds.Min.Mul(-1)) + scale := f32.Pt(float32(size.X), float32(size.Y)) + for i := range quad { + // Position quad to match place. + quad[i].posX += offsetf.X + quad[i].posY += offsetf.Y + // Scale to match viewport [0, 1]. + quad[i].posX /= scale.X + quad[i].posY /= scale.Y + } + // Draw quad as two triangles. + m.quads = append(m.quads, quad[0], quad[1], quad[3], quad[3], quad[1], quad[2]) + if m.allocs == nil { + m.allocs = make(map[textureKey]materialAlloc) + } + atlasAlloc := materialAlloc{ + alloc: &alloc, + offset: bounds.Min.Mul(-1), + } + atlas.allocs = append(atlas.allocs, atlasAlloc.alloc) + m.allocs[op.key] = atlasAlloc + op.matAlloc = atlasAlloc + texOps = texOps[1:] + } + if len(m.quads) == 0 { + break + } + realized := atlas.realized + if err := g.realizeAtlas(atlas, g.useCPU, atlas.packer.sizes[0]); err != nil { + return err + } + // Transform to clip space: [-1, -1] - [1, 1]. + *m.uniforms.u = materialUniforms{ + scale: [2]float32{2, 2}, + pos: [2]float32{-1, -1}, + } + if !g.srgb { + m.uniforms.u.emulatesRGB = 1.0 + } + m.uniforms.buf.Upload(byteslice.Struct(m.uniforms.u)) + vertexData := byteslice.Slice(m.quads) + n := pow2Ceil(len(vertexData)) + m.buffer.ensureCapacity(false, g.ctx, driver.BufferBindingVertices, n) + m.buffer.buffer.Upload(vertexData) + var d driver.LoadDesc + if !realized { + d.Action = driver.LoadActionClear + } + g.ctx.PrepareTexture(imgAtlas.image) + g.ctx.BeginRenderPass(atlas.image, d) + g.ctx.BindTexture(0, imgAtlas.image) + g.ctx.BindPipeline(m.pipeline) + g.ctx.BindUniforms(m.uniforms.buf) + g.ctx.BindVertexBuffer(m.buffer.buffer, 0) + newAllocs := atlas.allocs[allocStart:] + for i, a := range newAllocs { + sz := a.rect.Size().Sub(padding) + g.ctx.Viewport(a.rect.Min.X, a.rect.Min.Y, sz.X, sz.Y) + g.ctx.DrawArrays(i*6, 6) + } + g.ctx.EndRenderPass() + if !g.useCPU { + continue + } + src := atlas.image + data := atlas.cpuImage.Data() + for _, a := range newAllocs { + stride := atlas.size.X * 4 + col := a.rect.Min.X * 4 + row := stride * a.rect.Min.Y + off := col + row + src.ReadPixels(a.rect, data[off:], stride) + } + } + return nil +} + +func (g *compute) uploadImages() error { + for k, a := range g.imgAllocs { + if a.dead { + delete(g.imgAllocs, k) + } + } + type upload struct { + pos image.Point + img *image.RGBA + } + var uploads []upload + format := driver.TextureFormatSRGBA + if !g.srgb { + format = driver.TextureFormatRGBA8 + } + // padding is the number of pixels added to the right and below + // images, to avoid atlas filtering artifacts. + const padding = 1 + texOps := g.texOps + for len(texOps) > 0 { + uploads = uploads[:0] + var atlas *textureAtlas + for len(texOps) > 0 { + op := &texOps[0] + if a, exists := g.imgAllocs[op.img.handle]; exists { + g.touchAlloc(a) + op.imgAlloc = a + texOps = texOps[1:] + continue + } + size := op.img.src.Bounds().Size().Add(image.Pt(padding, padding)) + alloc, fits := g.atlasAlloc(allocQuery{ + atlas: atlas, + size: size, + format: format, + bindings: driver.BufferBindingTexture | driver.BufferBindingFramebuffer, + }) + if !fits { + break + } + atlas = alloc.atlas + if g.imgAllocs == nil { + g.imgAllocs = make(map[interface{}]*atlasAlloc) + } + op.imgAlloc = &alloc + atlas.allocs = append(atlas.allocs, op.imgAlloc) + g.imgAllocs[op.img.handle] = op.imgAlloc + uploads = append(uploads, upload{pos: alloc.rect.Min, img: op.img.src}) + texOps = texOps[1:] + } + if len(uploads) == 0 { + break + } + if err := g.realizeAtlas(atlas, false, atlas.packer.sizes[0]); err != nil { + return err + } + for _, u := range uploads { + size := u.img.Bounds().Size() + driver.UploadImage(atlas.image, u.pos, u.img) + rightPadding := image.Pt(padding, size.Y) + atlas.image.Upload(image.Pt(u.pos.X+size.X, u.pos.Y), rightPadding, g.zeros(rightPadding.X*rightPadding.Y*4), 0) + bottomPadding := image.Pt(size.X, padding) + atlas.image.Upload(image.Pt(u.pos.X, u.pos.Y+size.Y), bottomPadding, g.zeros(bottomPadding.X*bottomPadding.Y*4), 0) + } + } + return nil +} + +func pow2Ceil(v int) int { + exp := bits.Len(uint(v)) + if bits.OnesCount(uint(v)) == 1 { + exp-- + } + return 1 << exp +} + +// materialQuad constructs a quad that represents the transformed image. It returns the quad +// and its bounds. +func (g *compute) materialQuad(imgAtlasSize image.Point, M f32.Affine2D, img imageOpData, uvPos image.Point) [4]materialVertex { + imgSize := layout.FPt(img.src.Bounds().Size()) + sx, hx, ox, hy, sy, oy := M.Elems() + transOff := f32.Pt(ox, oy) + // The 4 corners of the image rectangle transformed by M, excluding its offset, are: + // + // q0: M * (0, 0) q3: M * (w, 0) + // q1: M * (0, h) q2: M * (w, h) + // + // Note that q0 = M*0 = 0, q2 = q1 + q3. + q0 := f32.Pt(0, 0) + q1 := f32.Pt(hx*imgSize.Y, sy*imgSize.Y) + q3 := f32.Pt(sx*imgSize.X, hy*imgSize.X) + q2 := q1.Add(q3) + q0 = q0.Add(transOff) + q1 = q1.Add(transOff) + q2 = q2.Add(transOff) + q3 = q3.Add(transOff) + + uvPosf := layout.FPt(uvPos) + atlasScale := f32.Pt(1/float32(imgAtlasSize.X), 1/float32(imgAtlasSize.Y)) + uvBounds := f32.Rectangle{ + Min: uvPosf, + Max: uvPosf.Add(imgSize), + } + uvBounds.Min.X *= atlasScale.X + uvBounds.Min.Y *= atlasScale.Y + uvBounds.Max.X *= atlasScale.X + uvBounds.Max.Y *= atlasScale.Y + quad := [4]materialVertex{ + {posX: q0.X, posY: q0.Y, u: uvBounds.Min.X, v: uvBounds.Min.Y}, + {posX: q1.X, posY: q1.Y, u: uvBounds.Min.X, v: uvBounds.Max.Y}, + {posX: q2.X, posY: q2.Y, u: uvBounds.Max.X, v: uvBounds.Max.Y}, + {posX: q3.X, posY: q3.Y, u: uvBounds.Max.X, v: uvBounds.Min.Y}, + } + return quad +} + +func quadBounds(q [4]materialVertex) f32.Rectangle { + q0 := f32.Pt(q[0].posX, q[0].posY) + q1 := f32.Pt(q[1].posX, q[1].posY) + q2 := f32.Pt(q[2].posX, q[2].posY) + q3 := f32.Pt(q[3].posX, q[3].posY) + return f32.Rectangle{ + Min: min(min(q0, q1), min(q2, q3)), + Max: max(max(q0, q1), max(q2, q3)), + } +} + +func max(p1, p2 f32.Point) f32.Point { + p := p1 + if p2.X > p.X { + p.X = p2.X + } + if p2.Y > p.Y { + p.Y = p2.Y + } + return p +} + +func min(p1, p2 f32.Point) f32.Point { + p := p1 + if p2.X < p.X { + p.X = p2.X + } + if p2.Y < p.Y { + p.Y = p2.Y + } + return p +} + +func (enc *encoder) encodePath(verts []byte, fillMode int) { + for ; len(verts) >= scene.CommandSize+4; verts = verts[scene.CommandSize+4:] { + cmd := ops.DecodeCommand(verts[4:]) + if cmd.Op() == scene.OpGap { + if fillMode != scene.FillModeNonzero { + // Skip gaps in strokes. + continue + } + // Replace them by a straight line in outlines. + cmd = scene.Line(scene.DecodeGap(cmd)) + } + enc.scene = append(enc.scene, cmd) + enc.npathseg++ + } +} + +func (g *compute) render(images *textureAtlas, dst driver.Texture, cpuDst cpu.ImageDescriptor, tileDims image.Point, stride int) error { + const ( + // wgSize is the largest and most common workgroup size. + wgSize = 128 + // PARTITION_SIZE from elements.comp + partitionSize = 32 * 4 + ) + widthInBins := (tileDims.X + 15) / 16 + heightInBins := (tileDims.Y + 7) / 8 + if widthInBins*heightInBins > wgSize { + return fmt.Errorf("gpu: output too large (%dx%d)", tileDims.X*tileWidthPx, tileDims.Y*tileHeightPx) + } + + enc := &g.enc + // Pad scene with zeroes to avoid reading garbage in elements.comp. + scenePadding := partitionSize - len(enc.scene)%partitionSize + enc.scene = append(enc.scene, make([]scene.Command, scenePadding)...) + + scene := byteslice.Slice(enc.scene) + if s := len(scene); s > g.buffers.scene.size { + paddedCap := s * 11 / 10 + if err := g.buffers.scene.ensureCapacity(g.useCPU, g.ctx, driver.BufferBindingShaderStorageRead, paddedCap); err != nil { + return err + } + } + g.buffers.scene.upload(scene) + + // alloc is the number of allocated bytes for static buffers. + var alloc uint32 + round := func(v, quantum int) int { + return (v + quantum - 1) &^ (quantum - 1) + } + malloc := func(size int) memAlloc { + size = round(size, 4) + offset := alloc + alloc += uint32(size) + return memAlloc{offset /*, uint32(size)*/} + } + + *g.conf = config{ + n_elements: uint32(enc.npath), + n_pathseg: uint32(enc.npathseg), + width_in_tiles: uint32(tileDims.X), + height_in_tiles: uint32(tileDims.Y), + tile_alloc: malloc(enc.npath * pathSize), + bin_alloc: malloc(round(enc.npath, wgSize) * binSize), + ptcl_alloc: malloc(tileDims.X * tileDims.Y * ptclInitialAlloc), + pathseg_alloc: malloc(enc.npathseg * pathsegSize), + anno_alloc: malloc(enc.npath * annoSize), + trans_alloc: malloc(enc.ntrans * transSize), + } + + numPartitions := (enc.numElements() + 127) / 128 + // clearSize is the atomic partition counter plus flag and 2 states per partition. + clearSize := 4 + numPartitions*stateStride + if clearSize > g.buffers.state.size { + paddedCap := clearSize * 11 / 10 + if err := g.buffers.state.ensureCapacity(g.useCPU, g.ctx, driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite, paddedCap); err != nil { + return err + } + } + + confData := byteslice.Struct(g.conf) + g.buffers.config.ensureCapacity(g.useCPU, g.ctx, driver.BufferBindingShaderStorageRead, len(confData)) + g.buffers.config.upload(confData) + + minSize := int(unsafe.Sizeof(memoryHeader{})) + int(alloc) + if minSize > g.buffers.memory.size { + // Add space for dynamic GPU allocations. + const sizeBump = 4 * 1024 * 1024 + minSize += sizeBump + if err := g.buffers.memory.ensureCapacity(g.useCPU, g.ctx, driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite, minSize); err != nil { + return err + } + } + + for { + *g.memHeader = memoryHeader{ + mem_offset: alloc, + } + g.buffers.memory.upload(byteslice.Struct(g.memHeader)) + g.buffers.state.upload(g.zeros(clearSize)) + + if !g.useCPU { + g.ctx.BeginCompute() + g.ctx.BindImageTexture(kernel4OutputUnit, dst) + img := g.output.nullMaterials + if images != nil { + img = images.image + } + g.ctx.BindImageTexture(kernel4AtlasUnit, img) + } else { + *g.output.descriptors.Binding2() = cpuDst + if images != nil { + *g.output.descriptors.Binding3() = images.cpuImage + } + } + + g.bindBuffers() + g.memoryBarrier() + g.dispatch(g.programs.elements, numPartitions, 1, 1) + g.memoryBarrier() + g.dispatch(g.programs.tileAlloc, (enc.npath+wgSize-1)/wgSize, 1, 1) + g.memoryBarrier() + g.dispatch(g.programs.pathCoarse, (enc.npathseg+31)/32, 1, 1) + g.memoryBarrier() + g.dispatch(g.programs.backdrop, (enc.npath+wgSize-1)/wgSize, 1, 1) + // No barrier needed between backdrop and binning. + g.dispatch(g.programs.binning, (enc.npath+wgSize-1)/wgSize, 1, 1) + g.memoryBarrier() + g.dispatch(g.programs.coarse, widthInBins, heightInBins, 1) + g.memoryBarrier() + g.dispatch(g.programs.kernel4, tileDims.X, tileDims.Y, 1) + g.memoryBarrier() + if !g.useCPU { + g.ctx.EndCompute() + } else { + g.dispatcher.Sync() + } + + if err := g.buffers.memory.download(byteslice.Struct(g.memHeader)); err != nil { + if err == driver.ErrContentLost { + continue + } + return err + } + switch errCode := g.memHeader.mem_error; errCode { + case memNoError: + if g.useCPU { + w, h := tileDims.X*tileWidthPx, tileDims.Y*tileHeightPx + dst.Upload(image.Pt(0, 0), image.Pt(w, h), cpuDst.Data(), stride) + } + return nil + case memMallocFailed: + // Resize memory and try again. + sz := g.buffers.memory.size * 15 / 10 + if err := g.buffers.memory.ensureCapacity(g.useCPU, g.ctx, driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite, sz); err != nil { + return err + } + continue + default: + return fmt.Errorf("compute: shader program failed with error %d", errCode) + } + } +} + +func (g *compute) memoryBarrier() { + if g.useCPU { + g.dispatcher.Barrier() + } +} + +func (g *compute) dispatch(p computeProgram, x, y, z int) { + if !g.useCPU { + g.ctx.BindProgram(p.prog) + g.ctx.DispatchCompute(x, y, z) + } else { + g.dispatcher.Dispatch(p.progInfo, p.descriptors, x, y, z) + } +} + +// zeros returns a byte slice with size bytes of zeros. +func (g *compute) zeros(size int) []byte { + if cap(g.zeroSlice) < size { + g.zeroSlice = append(g.zeroSlice, make([]byte, size)...) + } + return g.zeroSlice[:size] +} + +func (g *compute) touchAlloc(a *atlasAlloc) { + if a.dead { + panic("re-use of dead allocation") + } + a.frameCount = g.frameCount + a.atlas.lastFrame = a.frameCount +} + +func (g *compute) atlasAlloc(q allocQuery) (atlasAlloc, bool) { + var ( + place placement + fits bool + atlas = q.atlas + ) + if atlas != nil { + place, fits = atlas.packer.tryAdd(q.size) + if !fits { + atlas.compact = true + } + } + if atlas == nil { + // Look for matching atlas to re-use. + for _, a := range g.atlases { + if q.empty && len(a.allocs) > 0 { + continue + } + if q.nocompact && a.compact { + continue + } + if a.format != q.format || a.bindings&q.bindings != q.bindings { + continue + } + place, fits = a.packer.tryAdd(q.size) + if !fits { + a.compact = true + continue + } + atlas = a + break + } + } + if atlas == nil { + atlas = &textureAtlas{ + format: q.format, + bindings: q.bindings, + } + atlas.packer.maxDims = image.Pt(g.maxTextureDim, g.maxTextureDim) + atlas.packer.newPage() + g.atlases = append(g.atlases, atlas) + place, fits = atlas.packer.tryAdd(q.size) + if !fits { + panic(fmt.Errorf("compute: atlas allocation too large (%v)", q.size)) + } + } + if !fits { + return atlasAlloc{}, false + } + atlas.lastFrame = g.frameCount + return atlasAlloc{ + frameCount: g.frameCount, + atlas: atlas, + rect: image.Rectangle{Min: place.Pos, Max: place.Pos.Add(q.size)}, + }, true +} + +func (g *compute) realizeAtlas(atlas *textureAtlas, useCPU bool, size image.Point) error { + defer func() { + atlas.packer.maxDims = atlas.size + atlas.realized = true + atlas.ensureCPUImage(useCPU) + }() + if atlas.size.X >= size.X && atlas.size.Y >= size.Y { + return nil + } + if atlas.realized { + panic("resizing a realized atlas") + } + if err := atlas.resize(g.ctx, size); err != nil { + return err + } + return nil +} + +func (a *textureAtlas) resize(ctx driver.Device, size image.Point) error { + a.Release() + + img, err := ctx.NewTexture(a.format, size.X, size.Y, + driver.FilterNearest, + driver.FilterNearest, + a.bindings) + if err != nil { + return err + } + a.image = img + a.size = size + return nil +} + +func (a *textureAtlas) ensureCPUImage(useCPU bool) { + if !useCPU || a.hasCPU { + return + } + a.hasCPU = true + a.cpuImage = cpu.NewImageRGBA(a.size.X, a.size.Y) +} + +func (g *compute) Release() { + if g.useCPU { + g.dispatcher.Stop() + } + type resource interface { + Release() + } + res := []resource{ + g.output.nullMaterials, + &g.programs.elements, + &g.programs.tileAlloc, + &g.programs.pathCoarse, + &g.programs.backdrop, + &g.programs.binning, + &g.programs.coarse, + &g.programs.kernel4, + g.output.blitPipeline, + &g.output.buffer, + g.output.uniBuf, + &g.buffers.scene, + &g.buffers.state, + &g.buffers.memory, + &g.buffers.config, + g.materials.pipeline, + &g.materials.buffer, + g.materials.uniforms.buf, + g.timers.t, + } + for _, r := range res { + if r != nil { + r.Release() + } + } + for _, a := range g.atlases { + a.Release() + } + g.ctx.Release() + *g = compute{} +} + +func (a *textureAtlas) Release() { + if a.image != nil { + a.image.Release() + a.image = nil + } + a.cpuImage.Free() + a.hasCPU = false +} + +func (g *compute) bindBuffers() { + g.bindStorageBuffers(g.programs.elements, g.buffers.memory, g.buffers.config, g.buffers.scene, g.buffers.state) + g.bindStorageBuffers(g.programs.tileAlloc, g.buffers.memory, g.buffers.config) + g.bindStorageBuffers(g.programs.pathCoarse, g.buffers.memory, g.buffers.config) + g.bindStorageBuffers(g.programs.backdrop, g.buffers.memory, g.buffers.config) + g.bindStorageBuffers(g.programs.binning, g.buffers.memory, g.buffers.config) + g.bindStorageBuffers(g.programs.coarse, g.buffers.memory, g.buffers.config) + g.bindStorageBuffers(g.programs.kernel4, g.buffers.memory, g.buffers.config) +} + +func (p *computeProgram) Release() { + if p.prog != nil { + p.prog.Release() + } + *p = computeProgram{} +} + +func (b *sizedBuffer) Release() { + if b.buffer != nil { + b.buffer.Release() + } + b.cpuBuf.Free() + *b = sizedBuffer{} +} + +func (b *sizedBuffer) ensureCapacity(useCPU bool, ctx driver.Device, binding driver.BufferBinding, size int) error { + if b.size >= size { + return nil + } + if b.buffer != nil { + b.Release() + } + b.cpuBuf.Free() + if !useCPU { + buf, err := ctx.NewBuffer(binding, size) + if err != nil { + return err + } + b.buffer = buf + } else { + b.cpuBuf = cpu.NewBuffer(size) + } + b.size = size + return nil +} + +func (b *sizedBuffer) download(data []byte) error { + if b.buffer != nil { + return b.buffer.Download(data) + } else { + copy(data, b.cpuBuf.Data()) + return nil + } +} + +func (b *sizedBuffer) upload(data []byte) { + if b.buffer != nil { + b.buffer.Upload(data) + } else { + copy(b.cpuBuf.Data(), data) + } +} + +func (g *compute) bindStorageBuffers(prog computeProgram, buffers ...sizedBuffer) { + for i, buf := range buffers { + if !g.useCPU { + g.ctx.BindStorageBuffer(i, buf.buffer) + } else { + *prog.buffers[i] = buf.cpuBuf + } + } +} + +var bo = binary.LittleEndian + +func (e *encoder) reset() { + e.scene = e.scene[:0] + e.npath = 0 + e.npathseg = 0 + e.ntrans = 0 +} + +func (e *encoder) numElements() int { + return len(e.scene) +} + +func (e *encoder) append(e2 encoder) { + e.scene = append(e.scene, e2.scene...) + e.npath += e2.npath + e.npathseg += e2.npathseg + e.ntrans += e2.ntrans +} + +func (e *encoder) transform(m f32.Affine2D) { + e.scene = append(e.scene, scene.Transform(m)) + e.ntrans++ +} + +func (e *encoder) lineWidth(width float32) { + e.scene = append(e.scene, scene.SetLineWidth(width)) +} + +func (e *encoder) fillMode(mode scene.FillMode) { + e.scene = append(e.scene, scene.SetFillMode(mode)) +} + +func (e *encoder) beginClip(bbox f32.Rectangle) { + e.scene = append(e.scene, scene.BeginClip(bbox)) + e.npath++ +} + +func (e *encoder) endClip(bbox f32.Rectangle) { + e.scene = append(e.scene, scene.EndClip(bbox)) + e.npath++ +} + +func (e *encoder) rect(r f32.Rectangle) { + // Rectangle corners, clock-wise. + c0, c1, c2, c3 := r.Min, f32.Pt(r.Min.X, r.Max.Y), r.Max, f32.Pt(r.Max.X, r.Min.Y) + e.line(c0, c1) + e.line(c1, c2) + e.line(c2, c3) + e.line(c3, c0) +} + +func (e *encoder) fillColor(col color.RGBA) { + e.scene = append(e.scene, scene.FillColor(col)) + e.npath++ +} + +func (e *encoder) fillImage(index int, offset image.Point) { + e.scene = append(e.scene, scene.FillImage(index, offset)) + e.npath++ +} + +func (e *encoder) line(start, end f32.Point) { + e.scene = append(e.scene, scene.Line(start, end)) + e.npathseg++ +} + +func (e *encoder) quad(start, ctrl, end f32.Point) { + e.scene = append(e.scene, scene.Quad(start, ctrl, end)) + e.npathseg++ +} + +func (c *collector) reset() { + c.prevFrame, c.frame = c.frame, c.prevFrame + c.profile = false + c.clipStates = c.clipStates[:0] + c.transStack = c.transStack[:0] + c.frame.reset() +} + +func (c *opsCollector) reset() { + c.paths = c.paths[:0] + c.clipCmds = c.clipCmds[:0] + c.ops = c.ops[:0] + c.layers = c.layers[:0] +} + +func (c *collector) addClip(state *encoderState, viewport, bounds f32.Rectangle, path []byte, key ops.Key, hash uint64, strokeWidth float32, push bool) { + // Rectangle clip regions. + if len(path) == 0 && !push { + // If the rectangular clip region contains a previous path it can be discarded. + p := state.clip + t := state.relTrans.Invert() + for p != nil { + // rect is the parent bounds transformed relative to the rectangle. + rect := transformBounds(t, p.bounds) + if rect.In(bounds) { + return + } + t = p.relTrans.Invert().Mul(t) + p = p.parent + } + } + + absBounds := transformBounds(state.t, bounds).Bounds() + intersect := absBounds + if state.clip != nil { + intersect = state.clip.intersect.Intersect(intersect) + } + c.clipStates = append(c.clipStates, clipState{ + parent: state.clip, + absBounds: absBounds, + path: path, + pathKey: key, + intersect: intersect, + clipKey: clipKey{ + bounds: bounds, + relTrans: state.relTrans, + strokeWidth: strokeWidth, + pathHash: hash, + }, + }) + state.clip = &c.clipStates[len(c.clipStates)-1] + state.relTrans = f32.Affine2D{} +} + +func (c *collector) collect(root *op.Ops, viewport image.Point, texOps *[]textureOp) { + fview := f32.Rectangle{Max: layout.FPt(viewport)} + var intOps *ops.Ops + if root != nil { + intOps = &root.Internal + } + c.reader.Reset(intOps) + var state encoderState + reset := func() { + state = encoderState{ + paintKey: paintKey{ + color: color.NRGBA{A: 0xff}, + }, + } + } + reset() + r := &c.reader + var ( + pathData struct { + data []byte + key ops.Key + hash uint64 + } + strWidth float32 + ) + c.addClip(&state, fview, fview, nil, ops.Key{}, 0, 0, false) + for encOp, ok := r.Decode(); ok; encOp, ok = r.Decode() { + switch ops.OpType(encOp.Data[0]) { + case ops.TypeProfile: + c.profile = true + case ops.TypeTransform: + dop, push := ops.DecodeTransform(encOp.Data) + if push { + c.transStack = append(c.transStack, transEntry{t: state.t, relTrans: state.relTrans}) + } + state.t = state.t.Mul(dop) + state.relTrans = state.relTrans.Mul(dop) + case ops.TypePopTransform: + n := len(c.transStack) + st := c.transStack[n-1] + c.transStack = c.transStack[:n-1] + state.t = st.t + state.relTrans = st.relTrans + case ops.TypeStroke: + strWidth = decodeStrokeOp(encOp.Data) + case ops.TypePath: + hash := bo.Uint64(encOp.Data[1:]) + encOp, ok = r.Decode() + if !ok { + panic("unexpected end of path operation") + } + pathData.data = encOp.Data[ops.TypeAuxLen:] + pathData.key = encOp.Key + pathData.hash = hash + case ops.TypeClip: + var op ops.ClipOp + op.Decode(encOp.Data) + bounds := layout.FRect(op.Bounds) + c.addClip(&state, fview, bounds, pathData.data, pathData.key, pathData.hash, strWidth, true) + pathData.data = nil + strWidth = 0 + case ops.TypePopClip: + state.relTrans = state.clip.relTrans.Mul(state.relTrans) + state.clip = state.clip.parent + case ops.TypeColor: + state.matType = materialColor + state.color = decodeColorOp(encOp.Data) + case ops.TypeLinearGradient: + state.matType = materialLinearGradient + op := decodeLinearGradientOp(encOp.Data) + state.stop1 = op.stop1 + state.stop2 = op.stop2 + state.color1 = op.color1 + state.color2 = op.color2 + case ops.TypeImage: + state.matType = materialTexture + state.image = decodeImageOp(encOp.Data, encOp.Refs) + case ops.TypePaint: + paintState := state + if paintState.matType == materialTexture { + // Clip to the bounds of the image, to hide other images in the atlas. + sz := state.image.src.Rect.Size() + bounds := f32.Rectangle{Max: layout.FPt(sz)} + c.addClip(&paintState, fview, bounds, nil, ops.Key{}, 0, 0, false) + } + intersect := paintState.clip.intersect + if intersect.Empty() { + break + } + + // If the paint is a uniform opaque color that takes up the whole + // screen, it covers all previous paints and we can discard all + // rendering commands recorded so far. + if paintState.clip == nil && paintState.matType == materialColor && paintState.color.A == 255 { + c.clearColor = f32color.LinearFromSRGB(paintState.color).Opaque() + c.clear = true + c.frame.reset() + break + } + + // Flatten clip stack. + p := paintState.clip + startIdx := len(c.frame.clipCmds) + for p != nil { + idx := len(c.frame.paths) + c.frame.paths = append(c.frame.paths, make([]byte, len(p.path))...) + path := c.frame.paths[idx:] + copy(path, p.path) + c.frame.clipCmds = append(c.frame.clipCmds, clipCmd{ + state: p.clipKey, + path: path, + pathKey: p.pathKey, + absBounds: p.absBounds, + }) + p = p.parent + } + clipStack := c.frame.clipCmds[startIdx:] + c.frame.ops = append(c.frame.ops, paintOp{ + clipStack: clipStack, + state: paintState.paintKey, + intersect: intersect, + }) + case ops.TypeSave: + id := ops.DecodeSave(encOp.Data) + c.save(id, state.t) + case ops.TypeLoad: + reset() + id := ops.DecodeLoad(encOp.Data) + state.t = c.states[id] + state.relTrans = state.t + } + } + for i := range c.frame.ops { + op := &c.frame.ops[i] + // For each clip, cull rectangular clip regions that contain its + // (transformed) bounds. addClip already handled the converse case. + // TODO: do better than O(n²) to efficiently deal with deep stacks. + for j := 0; j < len(op.clipStack)-1; j++ { + cl := op.clipStack[j] + p := cl.state + r := transformBounds(p.relTrans, p.bounds) + for k := j + 1; k < len(op.clipStack); k++ { + cl2 := op.clipStack[k] + p2 := cl2.state + if len(cl2.path) == 0 && r.In(cl2.state.bounds) { + op.clipStack = append(op.clipStack[:k], op.clipStack[k+1:]...) + k-- + op.clipStack[k].state.relTrans = p2.relTrans.Mul(op.clipStack[k].state.relTrans) + } + r = transformRect(p2.relTrans, r) + } + } + // Separate the integer offset from the first transform. Two ops that differ + // only in integer offsets may share backing storage. + if len(op.clipStack) > 0 { + c := &op.clipStack[len(op.clipStack)-1] + t := c.state.relTrans + t, off := separateTransform(t) + c.state.relTrans = t + op.offset = off + op.state.t = op.state.t.Offset(layout.FPt(off.Mul(-1))) + } + op.hash = c.hashOp(*op) + op.texOpIdx = -1 + switch op.state.matType { + case materialTexture: + op.texOpIdx = len(*texOps) + // Separate integer offset from transformation. TextureOps that have identical transforms + // except for their integer offsets can share a transformed image. + t := op.state.t.Offset(layout.FPt(op.offset)) + t, off := separateTransform(t) + bounds := boundRectF(op.intersect).Sub(off) + *texOps = append(*texOps, textureOp{ + img: op.state.image, + off: off, + key: textureKey{ + bounds: bounds, + transform: t, + handle: op.state.image.handle, + }, + }) + } + } +} + +func (c *collector) hashOp(op paintOp) uint64 { + c.hasher.Reset() + for _, cl := range op.clipStack { + k := cl.state + keyBytes := (*[unsafe.Sizeof(k)]byte)(unsafe.Pointer(unsafe.Pointer(&k))) + c.hasher.Write(keyBytes[:]) + } + k := op.state + keyBytes := (*[unsafe.Sizeof(k)]byte)(unsafe.Pointer(unsafe.Pointer(&k))) + c.hasher.Write(keyBytes[:]) + return c.hasher.Sum64() +} + +func (g *compute) layer(viewport image.Point, texOps []textureOp) { + // Sort ops from previous frames by hash. + c := &g.collector + prevOps := c.prevFrame.ops + c.order = c.order[:0] + for i, op := range prevOps { + c.order = append(c.order, hashIndex{ + index: i, + hash: op.hash, + }) + } + sort.Slice(c.order, func(i, j int) bool { + return c.order[i].hash < c.order[j].hash + }) + // Split layers with different materials atlas; the compute stage has only + // one materials slot. + splitLayer := func(ops []paintOp, prevLayerIdx int) { + for len(ops) > 0 { + var materials *textureAtlas + idx := 0 + for idx < len(ops) { + if i := ops[idx].texOpIdx; i != -1 { + omats := texOps[i].matAlloc.alloc.atlas + if materials != nil && omats != nil && omats != materials { + break + } + materials = omats + } + idx++ + } + l := layer{ops: ops[:idx], materials: materials} + if prevLayerIdx != -1 { + prev := c.prevFrame.layers[prevLayerIdx] + if !prev.alloc.dead && len(prev.ops) == len(l.ops) { + l.alloc = prev.alloc + l.materials = prev.materials + g.touchAlloc(l.alloc) + } + } + for i, op := range l.ops { + l.rect = l.rect.Union(boundRectF(op.intersect)) + l.ops[i].layer = len(c.frame.layers) + } + c.frame.layers = append(c.frame.layers, l) + ops = ops[idx:] + } + } + ops := c.frame.ops + idx := 0 + for idx < len(ops) { + op := ops[idx] + // Search for longest matching op sequence. + // start is the earliest index of a match. + start := searchOp(c.order, op.hash) + layerOps, prevLayerIdx := longestLayer(prevOps, c.order[start:], ops[idx:]) + if len(layerOps) == 0 { + idx++ + continue + } + if unmatched := ops[:idx]; len(unmatched) > 0 { + // Flush layer of unmatched ops. + splitLayer(unmatched, -1) + ops = ops[idx:] + idx = 0 + } + splitLayer(layerOps, prevLayerIdx) + ops = ops[len(layerOps):] + } + if len(ops) > 0 { + splitLayer(ops, -1) + } +} + +func longestLayer(prev []paintOp, order []hashIndex, ops []paintOp) ([]paintOp, int) { + longest := 0 + longestIdx := -1 +outer: + for len(order) > 0 { + first := order[0] + order = order[1:] + match := prev[first.index:] + // Potential match found. Now find longest matching sequence. + end := 0 + layer := match[0].layer + off := match[0].offset.Sub(ops[0].offset) + for end < len(match) && end < len(ops) { + m := match[end] + o := ops[end] + // End layers on previous match. + if m.layer != layer { + break + } + // End layer when the next op doesn't match. + if m.hash != o.hash { + if end == 0 { + // Hashes are sorted so if the first op doesn't match, no + // more matches are possible. + break outer + } + break + } + if !opEqual(off, m, o) { + break + } + end++ + } + if end > longest { + longest = end + longestIdx = layer + + } + } + return ops[:longest], longestIdx +} + +func searchOp(order []hashIndex, hash uint64) int { + lo, hi := 0, len(order) + for lo < hi { + mid := (lo + hi) / 2 + if order[mid].hash < hash { + lo = mid + 1 + } else { + hi = mid + } + } + return lo +} + +func opEqual(off image.Point, o1 paintOp, o2 paintOp) bool { + if len(o1.clipStack) != len(o2.clipStack) { + return false + } + if o1.state != o2.state { + return false + } + if o1.offset.Sub(o2.offset) != off { + return false + } + for i, cl1 := range o1.clipStack { + cl2 := o2.clipStack[i] + if len(cl1.path) != len(cl2.path) { + return false + } + if cl1.state != cl2.state { + return false + } + if cl1.pathKey != cl2.pathKey && !bytes.Equal(cl1.path, cl2.path) { + return false + } + } + return true +} + +func encodeLayer(l layer, pos image.Point, viewport image.Point, enc *encoder, texOps []textureOp) { + off := pos.Sub(l.rect.Min) + offf := layout.FPt(off) + + enc.transform(f32.Affine2D{}.Offset(offf)) + for _, op := range l.ops { + encodeOp(viewport, off, enc, texOps, op) + } + enc.transform(f32.Affine2D{}.Offset(offf.Mul(-1))) +} + +func encodeOp(viewport image.Point, absOff image.Point, enc *encoder, texOps []textureOp, op paintOp) { + // Fill in clip bounds, which the shaders expect to be the union + // of all affected bounds. + var union f32.Rectangle + for i, cl := range op.clipStack { + union = union.Union(cl.absBounds) + op.clipStack[i].union = union + } + + absOfff := layout.FPt(absOff) + fillMode := scene.FillModeNonzero + opOff := layout.FPt(op.offset) + inv := f32.Affine2D{}.Offset(opOff) + enc.transform(inv) + for i := len(op.clipStack) - 1; i >= 0; i-- { + cl := op.clipStack[i] + if w := cl.state.strokeWidth; w > 0 { + enc.fillMode(scene.FillModeStroke) + enc.lineWidth(w) + fillMode = scene.FillModeStroke + } else if fillMode != scene.FillModeNonzero { + enc.fillMode(scene.FillModeNonzero) + fillMode = scene.FillModeNonzero + } + enc.transform(cl.state.relTrans) + inv = inv.Mul(cl.state.relTrans) + if len(cl.path) == 0 { + enc.rect(cl.state.bounds) + } else { + enc.encodePath(cl.path, fillMode) + } + if i != 0 { + enc.beginClip(cl.union.Add(absOfff)) + } + } + if len(op.clipStack) == 0 { + // No clipping; fill the entire view. + enc.rect(f32.Rectangle{Max: layout.FPt(viewport)}) + } + + switch op.state.matType { + case materialTexture: + texOp := texOps[op.texOpIdx] + off := texOp.matAlloc.alloc.rect.Min.Add(texOp.matAlloc.offset).Sub(texOp.off).Sub(absOff) + enc.fillImage(0, off) + case materialColor: + enc.fillColor(f32color.NRGBAToRGBA(op.state.color)) + case materialLinearGradient: + // TODO: implement. + enc.fillColor(f32color.NRGBAToRGBA(op.state.color1)) + default: + panic("not implemented") + } + enc.transform(inv.Invert()) + // Pop the clip stack, except the first entry used for fill. + for i := 1; i < len(op.clipStack); i++ { + cl := op.clipStack[i] + enc.endClip(cl.union.Add(absOfff)) + } + if fillMode != scene.FillModeNonzero { + enc.fillMode(scene.FillModeNonzero) + } +} + +func (c *collector) save(id int, state f32.Affine2D) { + if extra := id - len(c.states) + 1; extra > 0 { + c.states = append(c.states, make([]f32.Affine2D, extra)...) + } + c.states[id] = state +} + +func transformBounds(t f32.Affine2D, bounds f32.Rectangle) rectangle { + return rectangle{ + t.Transform(bounds.Min), t.Transform(f32.Pt(bounds.Max.X, bounds.Min.Y)), + t.Transform(bounds.Max), t.Transform(f32.Pt(bounds.Min.X, bounds.Max.Y)), + } +} + +func separateTransform(t f32.Affine2D) (f32.Affine2D, image.Point) { + sx, hx, ox, hy, sy, oy := t.Elems() + intx, fracx := math.Modf(float64(ox)) + inty, fracy := math.Modf(float64(oy)) + t = f32.NewAffine2D(sx, hx, float32(fracx), hy, sy, float32(fracy)) + return t, image.Pt(int(intx), int(inty)) +} + +func transformRect(t f32.Affine2D, r rectangle) rectangle { + var tr rectangle + for i, c := range r { + tr[i] = t.Transform(c) + } + return tr +} + +func (r rectangle) In(b f32.Rectangle) bool { + for _, c := range r { + inside := b.Min.X <= c.X && c.X <= b.Max.X && + b.Min.Y <= c.Y && c.Y <= b.Max.Y + if !inside { + return false + } + } + return true +} + +func (r rectangle) Contains(b f32.Rectangle) bool { + return true +} + +func (r rectangle) Bounds() f32.Rectangle { + bounds := f32.Rectangle{ + Min: f32.Pt(math.MaxFloat32, math.MaxFloat32), + Max: f32.Pt(-math.MaxFloat32, -math.MaxFloat32), + } + for _, c := range r { + if c.X < bounds.Min.X { + bounds.Min.X = c.X + } + if c.Y < bounds.Min.Y { + bounds.Min.Y = c.Y + } + if c.X > bounds.Max.X { + bounds.Max.X = c.X + } + if c.Y > bounds.Max.Y { + bounds.Max.Y = c.Y + } + } + return bounds +} diff --git a/vendor/gioui.org/gpu/cpu.go b/vendor/gioui.org/gpu/cpu.go new file mode 100644 index 0000000..f2f84ad --- /dev/null +++ b/vendor/gioui.org/gpu/cpu.go @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package gpu + +import ( + "unsafe" + + "gioui.org/cpu" +) + +// This file contains code specific to running compute shaders on the CPU. + +// dispatcher dispatches CPU compute programs across multiple goroutines. +type dispatcher struct { + // done is notified when a worker completes its work slice. + done chan struct{} + // work receives work slice indices. It is closed when the dispatcher is released. + work chan work + // dispatch receives compute jobs, which is then split among workers. + dispatch chan dispatch + // sync receives notification when a Sync completes. + sync chan struct{} +} + +type work struct { + ctx *cpu.DispatchContext + index int +} + +type dispatch struct { + _type jobType + program *cpu.ProgramInfo + descSet unsafe.Pointer + x, y, z int +} + +type jobType uint8 + +const ( + jobDispatch jobType = iota + jobBarrier + jobSync +) + +func newDispatcher(workers int) *dispatcher { + d := &dispatcher{ + work: make(chan work, workers), + done: make(chan struct{}, workers), + // Leave some room to avoid blocking calls to Dispatch. + dispatch: make(chan dispatch, 20), + sync: make(chan struct{}), + } + for i := 0; i < workers; i++ { + go d.worker() + } + go d.dispatcher() + return d +} + +func (d *dispatcher) dispatcher() { + defer close(d.work) + var free []*cpu.DispatchContext + defer func() { + for _, ctx := range free { + ctx.Free() + } + }() + var used []*cpu.DispatchContext + for job := range d.dispatch { + switch job._type { + case jobDispatch: + if len(free) == 0 { + free = append(free, cpu.NewDispatchContext()) + } + ctx := free[len(free)-1] + free = free[:len(free)-1] + used = append(used, ctx) + ctx.Prepare(cap(d.work), job.program, job.descSet, job.x, job.y, job.z) + for i := 0; i < cap(d.work); i++ { + d.work <- work{ + ctx: ctx, + index: i, + } + } + case jobBarrier: + // Wait for all outstanding dispatches to complete. + for i := 0; i < len(used)*cap(d.work); i++ { + <-d.done + } + free = append(free, used...) + used = used[:0] + case jobSync: + d.sync <- struct{}{} + } + } +} + +func (d *dispatcher) worker() { + thread := cpu.NewThreadContext() + defer thread.Free() + for w := range d.work { + w.ctx.Dispatch(w.index, thread) + d.done <- struct{}{} + } +} + +func (d *dispatcher) Barrier() { + d.dispatch <- dispatch{_type: jobBarrier} +} + +func (d *dispatcher) Sync() { + d.dispatch <- dispatch{_type: jobSync} + <-d.sync +} + +func (d *dispatcher) Dispatch(program *cpu.ProgramInfo, descSet unsafe.Pointer, x, y, z int) { + d.dispatch <- dispatch{ + _type: jobDispatch, + program: program, + descSet: descSet, + x: x, + y: y, + z: z, + } +} + +func (d *dispatcher) Stop() { + close(d.dispatch) +} diff --git a/vendor/gioui.org/gpu/gen.go b/vendor/gioui.org/gpu/gen.go deleted file mode 100644 index 709d0a7..0000000 --- a/vendor/gioui.org/gpu/gen.go +++ /dev/null @@ -1,5 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package gpu - -//go:generate go run ../internal/cmd/convertshaders -package gpu diff --git a/vendor/gioui.org/gpu/gl/backend.go b/vendor/gioui.org/gpu/gl/backend.go deleted file mode 100644 index 02037c3..0000000 --- a/vendor/gioui.org/gpu/gl/backend.go +++ /dev/null @@ -1,835 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package gl - -import ( - "errors" - "fmt" - "image" - "strings" - "time" - "unsafe" - - "gioui.org/gpu/backend" -) - -// Backend implements backend.Device. -type Backend struct { - funcs Functions - - state glstate - - glver [2]int - gles bool - ubo bool - feats backend.Caps - // floatTriple holds the settings for floating point - // textures. - floatTriple textureTriple - // Single channel alpha textures. - alphaTriple textureTriple - srgbaTriple textureTriple -} - -// State tracking. -type glstate struct { - // nattr is the current number of enabled vertex arrays. - nattr int - prog *gpuProgram - texUnits [2]*gpuTexture - layout *gpuInputLayout - buffer bufferBinding -} - -type bufferBinding struct { - buf *gpuBuffer - offset int - stride int -} - -type gpuTimer struct { - funcs Functions - obj Query -} - -type gpuTexture struct { - backend *Backend - obj Texture - triple textureTriple - width int - height int -} - -type gpuFramebuffer struct { - backend *Backend - obj Framebuffer - hasDepth bool - depthBuf Renderbuffer - foreign bool -} - -type gpuBuffer struct { - backend *Backend - hasBuffer bool - obj Buffer - typ backend.BufferBinding - size int - immutable bool - version int - // For emulation of uniform buffers. - data []byte -} - -type gpuProgram struct { - backend *Backend - obj Program - nattr int - vertUniforms uniformsTracker - fragUniforms uniformsTracker -} - -type uniformsTracker struct { - locs []uniformLocation - size int - buf *gpuBuffer - version int -} - -type uniformLocation struct { - uniform Uniform - offset int - typ backend.DataType - size int -} - -type gpuInputLayout struct { - inputs []backend.InputLocation - layout []backend.InputDesc -} - -// textureTriple holds the type settings for -// a TexImage2D call. -type textureTriple struct { - internalFormat int - format Enum - typ Enum -} - -func NewBackend(f Functions) (*Backend, error) { - exts := strings.Split(f.GetString(EXTENSIONS), " ") - glVer := f.GetString(VERSION) - ver, gles, err := ParseGLVersion(glVer) - if err != nil { - return nil, err - } - floatTriple, err := floatTripleFor(f, ver, exts) - if err != nil { - return nil, err - } - srgbaTriple, err := srgbaTripleFor(ver, exts) - if err != nil { - return nil, err - } - ubo := ver[0] >= 3 && gles - b := &Backend{ - glver: ver, - gles: gles, - ubo: ubo, - funcs: f, - floatTriple: floatTriple, - alphaTriple: alphaTripleFor(ver), - srgbaTriple: srgbaTriple, - } - if hasExtension(exts, "GL_EXT_disjoint_timer_query_webgl2") || hasExtension(exts, "GL_EXT_disjoint_timer_query") { - b.feats.Features |= backend.FeatureTimers - } - b.feats.MaxTextureSize = f.GetInteger(MAX_TEXTURE_SIZE) - return b, nil -} - -func (b *Backend) BeginFrame() { - // Assume GL state is reset between frames. - b.state = glstate{} -} - -func (b *Backend) EndFrame() { - b.funcs.ActiveTexture(TEXTURE0) -} - -func (b *Backend) Caps() backend.Caps { - return b.feats -} - -func (b *Backend) NewTimer() backend.Timer { - return &gpuTimer{ - funcs: b.funcs, - obj: b.funcs.CreateQuery(), - } -} - -func (b *Backend) IsTimeContinuous() bool { - return b.funcs.GetInteger(GPU_DISJOINT_EXT) == FALSE -} - -func (b *Backend) NewFramebuffer(tex backend.Texture, depthBits int) (backend.Framebuffer, error) { - glErr(b.funcs) - gltex := tex.(*gpuTexture) - fb := b.funcs.CreateFramebuffer() - fbo := &gpuFramebuffer{backend: b, obj: fb} - b.BindFramebuffer(fbo) - if err := glErr(b.funcs); err != nil { - fbo.Release() - return nil, err - } - b.funcs.FramebufferTexture2D(FRAMEBUFFER, COLOR_ATTACHMENT0, TEXTURE_2D, gltex.obj, 0) - if depthBits > 0 { - size := Enum(DEPTH_COMPONENT16) - switch { - case depthBits > 24: - size = DEPTH_COMPONENT32F - case depthBits > 16: - size = DEPTH_COMPONENT24 - } - depthBuf := b.funcs.CreateRenderbuffer() - b.funcs.BindRenderbuffer(RENDERBUFFER, depthBuf) - b.funcs.RenderbufferStorage(RENDERBUFFER, size, gltex.width, gltex.height) - b.funcs.FramebufferRenderbuffer(FRAMEBUFFER, DEPTH_ATTACHMENT, RENDERBUFFER, depthBuf) - fbo.depthBuf = depthBuf - fbo.hasDepth = true - if err := glErr(b.funcs); err != nil { - fbo.Release() - return nil, err - } - } - if st := b.funcs.CheckFramebufferStatus(FRAMEBUFFER); st != FRAMEBUFFER_COMPLETE { - fbo.Release() - return nil, fmt.Errorf("incomplete framebuffer, status = 0x%x, err = %d", st, b.funcs.GetError()) - } - return fbo, nil -} - -func (b *Backend) CurrentFramebuffer() backend.Framebuffer { - fboID := Framebuffer(b.funcs.GetBinding(FRAMEBUFFER_BINDING)) - return &gpuFramebuffer{backend: b, obj: fboID, foreign: true} -} - -func (b *Backend) NewTexture(format backend.TextureFormat, width, height int, minFilter, magFilter backend.TextureFilter, binding backend.BufferBinding) (backend.Texture, error) { - glErr(b.funcs) - tex := &gpuTexture{backend: b, obj: b.funcs.CreateTexture(), width: width, height: height} - switch format { - case backend.TextureFormatFloat: - tex.triple = b.floatTriple - case backend.TextureFormatSRGB: - tex.triple = b.srgbaTriple - default: - return nil, errors.New("unsupported texture format") - } - b.BindTexture(0, tex) - b.funcs.TexParameteri(TEXTURE_2D, TEXTURE_MAG_FILTER, toTexFilter(magFilter)) - b.funcs.TexParameteri(TEXTURE_2D, TEXTURE_MIN_FILTER, toTexFilter(minFilter)) - b.funcs.TexParameteri(TEXTURE_2D, TEXTURE_WRAP_S, CLAMP_TO_EDGE) - b.funcs.TexParameteri(TEXTURE_2D, TEXTURE_WRAP_T, CLAMP_TO_EDGE) - b.funcs.TexImage2D(TEXTURE_2D, 0, tex.triple.internalFormat, width, height, tex.triple.format, tex.triple.typ, nil) - if err := glErr(b.funcs); err != nil { - tex.Release() - return nil, err - } - return tex, nil -} - -func (b *Backend) NewBuffer(typ backend.BufferBinding, size int) (backend.Buffer, error) { - glErr(b.funcs) - buf := &gpuBuffer{backend: b, typ: typ, size: size} - if typ&backend.BufferBindingUniforms != 0 { - if typ != backend.BufferBindingUniforms { - return nil, errors.New("uniforms buffers cannot be bound as anything else") - } - if !b.ubo { - // GLES 2 doesn't support uniform buffers. - buf.data = make([]byte, size) - } - } - if typ&^backend.BufferBindingUniforms != 0 || b.ubo { - buf.hasBuffer = true - buf.obj = b.funcs.CreateBuffer() - if err := glErr(b.funcs); err != nil { - buf.Release() - return nil, err - } - } - return buf, nil -} - -func (b *Backend) NewImmutableBuffer(typ backend.BufferBinding, data []byte) (backend.Buffer, error) { - glErr(b.funcs) - obj := b.funcs.CreateBuffer() - buf := &gpuBuffer{backend: b, obj: obj, typ: typ, size: len(data), hasBuffer: true} - buf.Upload(data) - buf.immutable = true - if err := glErr(b.funcs); err != nil { - buf.Release() - return nil, err - } - return buf, nil -} - -func glErr(f Functions) error { - if st := f.GetError(); st != NO_ERROR { - return fmt.Errorf("glGetError: %#x", st) - } - return nil -} - -func (b *Backend) bindTexture(unit int, t *gpuTexture) { - if b.state.texUnits[unit] != t { - b.funcs.ActiveTexture(TEXTURE0 + Enum(unit)) - b.funcs.BindTexture(TEXTURE_2D, t.obj) - b.state.texUnits[unit] = t - } -} - -func (b *Backend) useProgram(p *gpuProgram) { - if b.state.prog != p { - p.backend.funcs.UseProgram(p.obj) - b.state.prog = p - } -} - -func (b *Backend) enableVertexArrays(n int) { - // Enable needed arrays. - for i := b.state.nattr; i < n; i++ { - b.funcs.EnableVertexAttribArray(Attrib(i)) - } - // Disable extra arrays. - for i := n; i < b.state.nattr; i++ { - b.funcs.DisableVertexAttribArray(Attrib(i)) - } - b.state.nattr = n -} - -func (b *Backend) SetDepthTest(enable bool) { - if enable { - b.funcs.Enable(DEPTH_TEST) - } else { - b.funcs.Disable(DEPTH_TEST) - } -} - -func (b *Backend) BlendFunc(sfactor, dfactor backend.BlendFactor) { - b.funcs.BlendFunc(toGLBlendFactor(sfactor), toGLBlendFactor(dfactor)) -} - -func toGLBlendFactor(f backend.BlendFactor) Enum { - switch f { - case backend.BlendFactorOne: - return ONE - case backend.BlendFactorOneMinusSrcAlpha: - return ONE_MINUS_SRC_ALPHA - case backend.BlendFactorZero: - return ZERO - case backend.BlendFactorDstColor: - return DST_COLOR - default: - panic("unsupported blend factor") - } -} - -func (b *Backend) DepthMask(mask bool) { - b.funcs.DepthMask(mask) -} - -func (b *Backend) SetBlend(enable bool) { - if enable { - b.funcs.Enable(BLEND) - } else { - b.funcs.Disable(BLEND) - } -} - -func (b *Backend) DrawElements(mode backend.DrawMode, off, count int) { - b.prepareDraw() - // off is in 16-bit indices, but DrawElements take a byte offset. - byteOff := off * 2 - b.funcs.DrawElements(toGLDrawMode(mode), count, UNSIGNED_SHORT, byteOff) -} - -func (b *Backend) DrawArrays(mode backend.DrawMode, off, count int) { - b.prepareDraw() - b.funcs.DrawArrays(toGLDrawMode(mode), off, count) -} - -func (b *Backend) prepareDraw() { - nattr := b.state.prog.nattr - b.enableVertexArrays(nattr) - if nattr > 0 { - b.setupVertexArrays() - } - if p := b.state.prog; p != nil { - p.updateUniforms() - } -} - -func toGLDrawMode(mode backend.DrawMode) Enum { - switch mode { - case backend.DrawModeTriangleStrip: - return TRIANGLE_STRIP - case backend.DrawModeTriangles: - return TRIANGLES - default: - panic("unsupported draw mode") - } -} - -func (b *Backend) Viewport(x, y, width, height int) { - b.funcs.Viewport(x, y, width, height) -} - -func (b *Backend) Clear(colR, colG, colB, colA float32) { - b.funcs.ClearColor(colR, colG, colB, colA) - b.funcs.Clear(COLOR_BUFFER_BIT) -} - -func (b *Backend) ClearDepth(d float32) { - b.funcs.ClearDepthf(d) - b.funcs.Clear(DEPTH_BUFFER_BIT) -} - -func (b *Backend) DepthFunc(f backend.DepthFunc) { - var glfunc Enum - switch f { - case backend.DepthFuncGreater: - glfunc = GREATER - case backend.DepthFuncGreaterEqual: - glfunc = GEQUAL - default: - panic("unsupported depth func") - } - b.funcs.DepthFunc(glfunc) -} - -func (b *Backend) NewInputLayout(vs backend.ShaderSources, layout []backend.InputDesc) (backend.InputLayout, error) { - if len(vs.Inputs) != len(layout) { - return nil, fmt.Errorf("NewInputLayout: got %d inputs, expected %d", len(layout), len(vs.Inputs)) - } - for i, inp := range vs.Inputs { - if exp, got := inp.Size, layout[i].Size; exp != got { - return nil, fmt.Errorf("NewInputLayout: data size mismatch for %q: got %d expected %d", inp.Name, got, exp) - } - } - return &gpuInputLayout{ - inputs: vs.Inputs, - layout: layout, - }, nil -} - -func (b *Backend) NewProgram(vertShader, fragShader backend.ShaderSources) (backend.Program, error) { - attr := make([]string, len(vertShader.Inputs)) - for _, inp := range vertShader.Inputs { - attr[inp.Location] = inp.Name - } - vsrc, fsrc := vertShader.GLSL100ES, fragShader.GLSL100ES - if b.glver[0] >= 3 { - // OpenGL (ES) 3.0. - switch { - case b.gles: - vsrc, fsrc = vertShader.GLSL300ES, fragShader.GLSL300ES - case b.glver[0] >= 4 || b.glver[1] >= 2: - // OpenGL 3.2 Core only accepts glsl 1.50 or newer. - vsrc, fsrc = vertShader.GLSL150, fragShader.GLSL150 - default: - vsrc, fsrc = vertShader.GLSL130, fragShader.GLSL130 - } - } - p, err := CreateProgram(b.funcs, vsrc, fsrc, attr) - if err != nil { - return nil, err - } - gpuProg := &gpuProgram{ - backend: b, - obj: p, - nattr: len(attr), - } - b.BindProgram(gpuProg) - // Bind texture uniforms. - for _, tex := range vertShader.Textures { - u := b.funcs.GetUniformLocation(p, tex.Name) - if u.valid() { - b.funcs.Uniform1i(u, tex.Binding) - } - } - for _, tex := range fragShader.Textures { - u := b.funcs.GetUniformLocation(p, tex.Name) - if u.valid() { - b.funcs.Uniform1i(u, tex.Binding) - } - } - if b.ubo { - for _, block := range vertShader.Uniforms.Blocks { - blockIdx := b.funcs.GetUniformBlockIndex(p, block.Name) - if blockIdx != INVALID_INDEX { - b.funcs.UniformBlockBinding(p, blockIdx, uint(block.Binding)) - } - } - // To match Direct3D 11 with separate vertex and fragment - // shader uniform buffers, offset all fragment blocks to be - // located after the vertex blocks. - off := len(vertShader.Uniforms.Blocks) - for _, block := range fragShader.Uniforms.Blocks { - blockIdx := b.funcs.GetUniformBlockIndex(p, block.Name) - if blockIdx != INVALID_INDEX { - b.funcs.UniformBlockBinding(p, blockIdx, uint(block.Binding+off)) - } - } - } else { - gpuProg.vertUniforms.setup(b.funcs, p, vertShader.Uniforms.Size, vertShader.Uniforms.Locations) - gpuProg.fragUniforms.setup(b.funcs, p, fragShader.Uniforms.Size, fragShader.Uniforms.Locations) - } - return gpuProg, nil -} - -func lookupUniform(funcs Functions, p Program, loc backend.UniformLocation) uniformLocation { - u := GetUniformLocation(funcs, p, loc.Name) - return uniformLocation{uniform: u, offset: loc.Offset, typ: loc.Type, size: loc.Size} -} - -func (p *gpuProgram) SetVertexUniforms(buffer backend.Buffer) { - p.vertUniforms.setBuffer(buffer) -} - -func (p *gpuProgram) SetFragmentUniforms(buffer backend.Buffer) { - p.fragUniforms.setBuffer(buffer) -} - -func (p *gpuProgram) updateUniforms() { - f := p.backend.funcs - if p.backend.ubo { - if b := p.vertUniforms.buf; b != nil { - f.BindBufferBase(UNIFORM_BUFFER, 0, b.obj) - } - if b := p.fragUniforms.buf; b != nil { - f.BindBufferBase(UNIFORM_BUFFER, 1, b.obj) - } - } else { - p.vertUniforms.update(f) - p.fragUniforms.update(f) - } -} - -func (b *Backend) BindProgram(prog backend.Program) { - p := prog.(*gpuProgram) - b.useProgram(p) -} - -func (p *gpuProgram) Release() { - p.backend.funcs.DeleteProgram(p.obj) -} - -func (u *uniformsTracker) setup(funcs Functions, p Program, uniformSize int, uniforms []backend.UniformLocation) { - u.locs = make([]uniformLocation, len(uniforms)) - for i, uniform := range uniforms { - u.locs[i] = lookupUniform(funcs, p, uniform) - } - u.size = uniformSize -} - -func (u *uniformsTracker) setBuffer(buffer backend.Buffer) { - buf := buffer.(*gpuBuffer) - if buf.typ&backend.BufferBindingUniforms == 0 { - panic("not a uniform buffer") - } - if buf.size < u.size { - panic(fmt.Errorf("uniform buffer too small, got %d need %d", buf.size, u.size)) - } - u.buf = buf - // Force update. - u.version = buf.version - 1 -} - -func (p *uniformsTracker) update(funcs Functions) { - b := p.buf - if b == nil || b.version == p.version { - return - } - p.version = b.version - data := b.data - for _, u := range p.locs { - data := data[u.offset:] - switch { - case u.typ == backend.DataTypeFloat && u.size == 1: - data := data[:4] - v := *(*[1]float32)(unsafe.Pointer(&data[0])) - funcs.Uniform1f(u.uniform, v[0]) - case u.typ == backend.DataTypeFloat && u.size == 2: - data := data[:8] - v := *(*[2]float32)(unsafe.Pointer(&data[0])) - funcs.Uniform2f(u.uniform, v[0], v[1]) - case u.typ == backend.DataTypeFloat && u.size == 3: - data := data[:12] - v := *(*[3]float32)(unsafe.Pointer(&data[0])) - funcs.Uniform3f(u.uniform, v[0], v[1], v[2]) - case u.typ == backend.DataTypeFloat && u.size == 4: - data := data[:16] - v := *(*[4]float32)(unsafe.Pointer(&data[0])) - funcs.Uniform4f(u.uniform, v[0], v[1], v[2], v[3]) - default: - panic("unsupported uniform data type or size") - } - } -} - -func (b *gpuBuffer) Upload(data []byte) { - if b.immutable { - panic("immutable buffer") - } - if len(data) > b.size { - panic("buffer size overflow") - } - b.version++ - copy(b.data, data) - if b.hasBuffer { - firstBinding := firstBufferType(b.typ) - b.backend.funcs.BindBuffer(firstBinding, b.obj) - b.backend.funcs.BufferData(firstBinding, data, STATIC_DRAW) - } -} - -func (b *gpuBuffer) Release() { - if b.hasBuffer { - b.backend.funcs.DeleteBuffer(b.obj) - b.hasBuffer = false - } -} - -func (b *Backend) BindVertexBuffer(buf backend.Buffer, stride, offset int) { - gbuf := buf.(*gpuBuffer) - if gbuf.typ&backend.BufferBindingVertices == 0 { - panic("not a vertex buffer") - } - b.state.buffer = bufferBinding{buf: gbuf, stride: stride, offset: offset} -} - -func (b *Backend) setupVertexArrays() { - layout := b.state.layout - if layout == nil { - return - } - buf := b.state.buffer - b.funcs.BindBuffer(ARRAY_BUFFER, buf.buf.obj) - for i, inp := range layout.inputs { - l := layout.layout[i] - var gltyp Enum - switch l.Type { - case backend.DataTypeFloat: - gltyp = FLOAT - case backend.DataTypeShort: - gltyp = SHORT - default: - panic("unsupported data type") - } - b.funcs.VertexAttribPointer(Attrib(inp.Location), l.Size, gltyp, false, buf.stride, buf.offset+l.Offset) - } -} - -func (b *Backend) BindIndexBuffer(buf backend.Buffer) { - gbuf := buf.(*gpuBuffer) - if gbuf.typ&backend.BufferBindingIndices == 0 { - panic("not an index buffer") - } - b.funcs.BindBuffer(ELEMENT_ARRAY_BUFFER, gbuf.obj) -} - -func (f *gpuFramebuffer) ReadPixels(src image.Rectangle, pixels []byte) error { - glErr(f.backend.funcs) - f.backend.BindFramebuffer(f) - if len(pixels) < src.Dx()*src.Dy() { - return errors.New("unexpected RGBA size") - } - f.backend.funcs.ReadPixels(src.Min.X, src.Min.Y, src.Dx(), src.Dy(), RGBA, UNSIGNED_BYTE, pixels) - // OpenGL origin is in the lower-left corner. Flip the image to - // match. - flipImageY(src.Dx()*4, src.Dy(), pixels) - return glErr(f.backend.funcs) -} - -func flipImageY(stride int, height int, pixels []byte) { - // Flip image in y-direction. OpenGL's origin is in the lower - // left corner. - row := make([]uint8, stride) - for y := 0; y < height/2; y++ { - y1 := height - y - 1 - dest := y1 * stride - src := y * stride - copy(row, pixels[dest:]) - copy(pixels[dest:], pixels[src:src+len(row)]) - copy(pixels[src:], row) - } -} - -func (b *Backend) BindFramebuffer(fbo backend.Framebuffer) { - b.funcs.BindFramebuffer(FRAMEBUFFER, fbo.(*gpuFramebuffer).obj) -} - -func (f *gpuFramebuffer) Invalidate() { - f.backend.BindFramebuffer(f) - f.backend.funcs.InvalidateFramebuffer(FRAMEBUFFER, COLOR_ATTACHMENT0) -} - -func (f *gpuFramebuffer) Release() { - if f.foreign { - panic("cannot release framebuffer created by CurrentFramebuffer") - } - f.backend.funcs.DeleteFramebuffer(f.obj) - if f.hasDepth { - f.backend.funcs.DeleteRenderbuffer(f.depthBuf) - } -} - -func toTexFilter(f backend.TextureFilter) int { - switch f { - case backend.FilterNearest: - return NEAREST - case backend.FilterLinear: - return LINEAR - default: - panic("unsupported texture filter") - } -} - -func (b *Backend) BindTexture(unit int, t backend.Texture) { - b.bindTexture(unit, t.(*gpuTexture)) -} - -func (t *gpuTexture) Release() { - t.backend.funcs.DeleteTexture(t.obj) -} - -func (t *gpuTexture) Upload(img *image.RGBA) { - t.backend.BindTexture(0, t) - var pixels []byte - b := img.Bounds() - w, h := b.Dx(), b.Dy() - if img.Stride != w*4 { - panic("unsupported stride") - } - start := (b.Min.X + b.Min.Y*w) * 4 - end := (b.Max.X + (b.Max.Y-1)*w) * 4 - pixels = img.Pix[start:end] - t.backend.funcs.TexImage2D(TEXTURE_2D, 0, t.triple.internalFormat, w, h, t.triple.format, t.triple.typ, pixels) -} - -func (t *gpuTimer) Begin() { - t.funcs.BeginQuery(TIME_ELAPSED_EXT, t.obj) -} - -func (t *gpuTimer) End() { - t.funcs.EndQuery(TIME_ELAPSED_EXT) -} - -func (t *gpuTimer) ready() bool { - return t.funcs.GetQueryObjectuiv(t.obj, QUERY_RESULT_AVAILABLE) == TRUE -} - -func (t *gpuTimer) Release() { - t.funcs.DeleteQuery(t.obj) -} - -func (t *gpuTimer) Duration() (time.Duration, bool) { - if !t.ready() { - return 0, false - } - nanos := t.funcs.GetQueryObjectuiv(t.obj, QUERY_RESULT) - return time.Duration(nanos), true -} - -func (b *Backend) BindInputLayout(l backend.InputLayout) { - b.state.layout = l.(*gpuInputLayout) -} - -func (l *gpuInputLayout) Release() {} - -// floatTripleFor determines the best texture triple for floating point FBOs. -func floatTripleFor(f Functions, ver [2]int, exts []string) (textureTriple, error) { - var triples []textureTriple - if ver[0] >= 3 { - triples = append(triples, textureTriple{R16F, Enum(RED), Enum(HALF_FLOAT)}) - } - // According to the OES_texture_half_float specification, EXT_color_buffer_half_float is needed to - // render to FBOs. However, the Safari WebGL1 implementation does support half-float FBOs but does not - // report EXT_color_buffer_half_float support. The triples are verified below, so it doesn't matter if we're - // wrong. - if hasExtension(exts, "GL_OES_texture_half_float") || hasExtension(exts, "GL_EXT_color_buffer_half_float") { - // Try single channel. - triples = append(triples, textureTriple{LUMINANCE, Enum(LUMINANCE), Enum(HALF_FLOAT_OES)}) - // Fallback to 4 channels. - triples = append(triples, textureTriple{RGBA, Enum(RGBA), Enum(HALF_FLOAT_OES)}) - } - if hasExtension(exts, "GL_OES_texture_float") || hasExtension(exts, "GL_EXT_color_buffer_float") { - triples = append(triples, textureTriple{RGBA, Enum(RGBA), Enum(FLOAT)}) - } - tex := f.CreateTexture() - defer f.DeleteTexture(tex) - f.BindTexture(TEXTURE_2D, tex) - f.TexParameteri(TEXTURE_2D, TEXTURE_WRAP_S, CLAMP_TO_EDGE) - f.TexParameteri(TEXTURE_2D, TEXTURE_WRAP_T, CLAMP_TO_EDGE) - f.TexParameteri(TEXTURE_2D, TEXTURE_MAG_FILTER, NEAREST) - f.TexParameteri(TEXTURE_2D, TEXTURE_MIN_FILTER, NEAREST) - fbo := f.CreateFramebuffer() - defer f.DeleteFramebuffer(fbo) - defFBO := Framebuffer(f.GetBinding(FRAMEBUFFER_BINDING)) - f.BindFramebuffer(FRAMEBUFFER, fbo) - defer f.BindFramebuffer(FRAMEBUFFER, defFBO) - var attempts []string - for _, tt := range triples { - const size = 256 - f.TexImage2D(TEXTURE_2D, 0, tt.internalFormat, size, size, tt.format, tt.typ, nil) - f.FramebufferTexture2D(FRAMEBUFFER, COLOR_ATTACHMENT0, TEXTURE_2D, tex, 0) - st := f.CheckFramebufferStatus(FRAMEBUFFER) - if st == FRAMEBUFFER_COMPLETE { - return tt, nil - } - attempts = append(attempts, fmt.Sprintf("(0x%x, 0x%x, 0x%x): 0x%x", tt.internalFormat, tt.format, tt.typ, st)) - } - return textureTriple{}, fmt.Errorf("floating point fbos not supported (attempted %s)", attempts) -} - -func srgbaTripleFor(ver [2]int, exts []string) (textureTriple, error) { - switch { - case ver[0] >= 3: - return textureTriple{SRGB8_ALPHA8, Enum(RGBA), Enum(UNSIGNED_BYTE)}, nil - case hasExtension(exts, "GL_EXT_sRGB"): - return textureTriple{SRGB_ALPHA_EXT, Enum(SRGB_ALPHA_EXT), Enum(UNSIGNED_BYTE)}, nil - default: - return textureTriple{}, errors.New("no sRGB texture formats found") - } -} - -func alphaTripleFor(ver [2]int) textureTriple { - intf, f := R8, Enum(RED) - if ver[0] < 3 { - // R8, RED not supported on OpenGL ES 2.0. - intf, f = LUMINANCE, Enum(LUMINANCE) - } - return textureTriple{intf, f, UNSIGNED_BYTE} -} - -func hasExtension(exts []string, ext string) bool { - for _, e := range exts { - if ext == e { - return true - } - } - return false -} - -func firstBufferType(typ backend.BufferBinding) Enum { - switch { - case typ&backend.BufferBindingIndices != 0: - return ELEMENT_ARRAY_BUFFER - case typ&backend.BufferBindingVertices != 0: - return ARRAY_BUFFER - case typ&backend.BufferBindingUniforms != 0: - return UNIFORM_BUFFER - default: - panic("unsupported buffer type") - } -} diff --git a/vendor/gioui.org/gpu/gl/types.go b/vendor/gioui.org/gpu/gl/types.go deleted file mode 100644 index f986bba..0000000 --- a/vendor/gioui.org/gpu/gl/types.go +++ /dev/null @@ -1,27 +0,0 @@ -// +build !js - -package gl - -type ( - Buffer struct{ V uint } - Framebuffer struct{ V uint } - Program struct{ V uint } - Renderbuffer struct{ V uint } - Shader struct{ V uint } - Texture struct{ V uint } - Query struct{ V uint } - Uniform struct{ V int } - Object struct{ V uint } -) - -func (u Uniform) valid() bool { - return u.V != -1 -} - -func (p Program) valid() bool { - return p.V != 0 -} - -func (s Shader) valid() bool { - return s.V != 0 -} diff --git a/vendor/gioui.org/gpu/gl/types_js.go b/vendor/gioui.org/gpu/gl/types_js.go deleted file mode 100644 index 16e3a71..0000000 --- a/vendor/gioui.org/gpu/gl/types_js.go +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package gl - -import "syscall/js" - -type ( - Buffer js.Value - Framebuffer js.Value - Program js.Value - Renderbuffer js.Value - Shader js.Value - Texture js.Value - Query js.Value - Uniform js.Value - Object js.Value -) - -func (p Program) valid() bool { - return !js.Value(p).IsUndefined() && !js.Value(p).IsNull() -} - -func (s Shader) valid() bool { - return !js.Value(s).IsUndefined() && !js.Value(s).IsNull() -} - -func (u Uniform) valid() bool { - return !js.Value(u).IsUndefined() && !js.Value(u).IsNull() -} diff --git a/vendor/gioui.org/gpu/gpu.go b/vendor/gioui.org/gpu/gpu.go index 321b2a8..0bd15c6 100644 --- a/vendor/gioui.org/gpu/gpu.go +++ b/vendor/gioui.org/gpu/gpu.go @@ -13,36 +13,57 @@ import ( "image" "image/color" "math" + "os" "reflect" "time" "unsafe" "gioui.org/f32" - "gioui.org/gpu/backend" + "gioui.org/gpu/internal/driver" + "gioui.org/internal/byteslice" "gioui.org/internal/f32color" - "gioui.org/internal/opconst" "gioui.org/internal/ops" - gunsafe "gioui.org/internal/unsafe" + "gioui.org/internal/scene" + "gioui.org/internal/stroke" "gioui.org/layout" "gioui.org/op" - "gioui.org/op/paint" + "gioui.org/shader" + "gioui.org/shader/gio" + + // Register backends. + _ "gioui.org/gpu/internal/d3d11" + _ "gioui.org/gpu/internal/metal" + _ "gioui.org/gpu/internal/opengl" + _ "gioui.org/gpu/internal/vulkan" ) -type GPU struct { +type GPU interface { + // Release non-Go resources. The GPU is no longer valid after Release. + Release() + // Clear sets the clear color for the next Frame. + Clear(color color.NRGBA) + // Frame draws the graphics operations from op into a viewport of target. + Frame(frame *op.Ops, target RenderTarget, viewport image.Point) error + // Profile returns the last available profiling information. Profiling + // information is requested when Frame sees an io/profile.Op, and the result + // is available through Profile at some later time. + Profile() string +} + +type gpu struct { cache *resourceCache - defFBO backend.Framebuffer - profile string - timers *timers - frameStart time.Time - zopsTimer, stencilTimer, coverTimer, cleanupTimer *timer - drawOps drawOps - ctx backend.Device - renderer *renderer + profile string + timers *timers + frameStart time.Time + stencilTimer, coverTimer, cleanupTimer *timer + drawOps drawOps + ctx driver.Device + renderer *renderer } type renderer struct { - ctx backend.Device + ctx driver.Device blitter *blitter pather *pather packer packer @@ -50,17 +71,15 @@ type renderer struct { } type drawOps struct { - profile bool - reader ops.Reader - cache *resourceCache - vertCache []byte - viewport image.Point - clearColor f32color.RGBA - imageOps []imageOp - // zimageOps are the rectangle clipped opaque images - // that can use fast front-to-back rendering with z-test - // and no blending. - zimageOps []imageOp + profile bool + reader ops.Reader + states []f32.Affine2D + transStack []f32.Affine2D + vertCache []byte + viewport image.Point + clear bool + clearColor f32color.RGBA + imageOps []imageOp pathOps []*pathOp pathOpCache []pathOp qs quadSplitter @@ -68,26 +87,35 @@ type drawOps struct { } type drawState struct { - clip f32.Rectangle t f32.Affine2D cpath *pathOp - rect bool - z int matType materialType // Current paint.ImageOp image imageOpData // Current paint.ColorOp, if any. - color color.RGBA + color color.NRGBA + + // Current paint.LinearGradientOp. + stop1 f32.Point + stop2 f32.Point + color1 color.NRGBA + color2 color.NRGBA } type pathOp struct { off f32.Point + // rect tracks whether the clip stack can be represented by a + // pixel-aligned rectangle. + rect bool // clip is the union of all // later clip rectangles. - clip image.Rectangle - bounds f32.Rectangle - pathKey ops.Key + clip image.Rectangle + bounds f32.Rectangle + // intersect is the intersection of bounds and all + // previous clip bounds. + intersect f32.Rectangle + pathKey opKey path bool pathVerts []byte parent *pathOp @@ -95,87 +123,70 @@ type pathOp struct { } type imageOp struct { - z float32 path *pathOp - off f32.Point clip image.Rectangle material material clipType clipType place placement } +func decodeStrokeOp(data []byte) float32 { + _ = data[4] + bo := binary.LittleEndian + return math.Float32frombits(bo.Uint32(data[1:])) +} + +type quadsOp struct { + key opKey + aux []byte +} + +type opKey struct { + outline bool + strokeWidth float32 + sx, hx, sy, hy float32 + ops.Key +} + type material struct { material materialType opaque bool // For materialTypeColor. color f32color.RGBA + // For materialTypeLinearGradient. + color1 f32color.RGBA + color2 f32color.RGBA // For materialTypeTexture. - texture *texture + data imageOpData uvTrans f32.Affine2D } -// clipOp is the shadow of clip.Op. -type clipOp struct { - bounds f32.Rectangle -} - // imageOpData is the shadow of paint.ImageOp. type imageOpData struct { - rect image.Rectangle src *image.RGBA handle interface{} } -func (op *clipOp) decode(data []byte) { - if opconst.OpType(data[0]) != opconst.TypeClip { - panic("invalid op") - } - bo := binary.LittleEndian - r := f32.Rectangle{ - Min: f32.Point{ - X: math.Float32frombits(bo.Uint32(data[1:])), - Y: math.Float32frombits(bo.Uint32(data[5:])), - }, - Max: f32.Point{ - X: math.Float32frombits(bo.Uint32(data[9:])), - Y: math.Float32frombits(bo.Uint32(data[13:])), - }, - } - *op = clipOp{ - bounds: r, - } +type linearGradientOpData struct { + stop1 f32.Point + color1 color.NRGBA + stop2 f32.Point + color2 color.NRGBA } func decodeImageOp(data []byte, refs []interface{}) imageOpData { - if opconst.OpType(data[0]) != opconst.TypeImage { - panic("invalid op") - } handle := refs[1] if handle == nil { return imageOpData{} } - bo := binary.LittleEndian return imageOpData{ - rect: image.Rectangle{ - Min: image.Point{ - X: int(bo.Uint32(data[1:])), - Y: int(bo.Uint32(data[5:])), - }, - Max: image.Point{ - X: int(bo.Uint32(data[9:])), - Y: int(bo.Uint32(data[13:])), - }, - }, src: refs[0].(*image.RGBA), handle: handle, } } -func decodeColorOp(data []byte) color.RGBA { - if opconst.OpType(data[0]) != opconst.TypeColor { - panic("invalid op") - } - return color.RGBA{ +func decodeColorOp(data []byte) color.NRGBA { + return color.NRGBA{ R: data[1], G: data[2], B: data[3], @@ -183,23 +194,29 @@ func decodeColorOp(data []byte) color.RGBA { } } -func decodePaintOp(data []byte) paint.PaintOp { +func decodeLinearGradientOp(data []byte) linearGradientOpData { bo := binary.LittleEndian - if opconst.OpType(data[0]) != opconst.TypePaint { - panic("invalid op") - } - r := f32.Rectangle{ - Min: f32.Point{ + return linearGradientOpData{ + stop1: f32.Point{ X: math.Float32frombits(bo.Uint32(data[1:])), Y: math.Float32frombits(bo.Uint32(data[5:])), }, - Max: f32.Point{ + stop2: f32.Point{ X: math.Float32frombits(bo.Uint32(data[9:])), Y: math.Float32frombits(bo.Uint32(data[13:])), }, - } - return paint.PaintOp{ - Rect: r, + color1: color.NRGBA{ + R: data[17+0], + G: data[17+1], + B: data[17+2], + A: data[17+3], + }, + color2: color.NRGBA{ + R: data[21+0], + G: data[21+1], + B: data[21+2], + A: data[21+3], + }, } } @@ -211,58 +228,60 @@ type resource interface { type texture struct { src *image.RGBA - tex backend.Texture + tex driver.Texture } type blitter struct { - ctx backend.Device - viewport image.Point - prog [2]*program - layout backend.InputLayout - colUniforms *blitColUniforms - texUniforms *blitTexUniforms - quadVerts backend.Buffer + ctx driver.Device + viewport image.Point + pipelines [3]*pipeline + colUniforms *blitColUniforms + texUniforms *blitTexUniforms + linearGradientUniforms *blitLinearGradientUniforms + quadVerts driver.Buffer } type blitColUniforms struct { - vert struct { - blitUniforms - _ [12]byte // Padding to a multiple of 16. - } - frag struct { - colorUniforms - } + blitUniforms + _ [128 - unsafe.Sizeof(blitUniforms{}) - unsafe.Sizeof(colorUniforms{})]byte // Padding to 128 bytes. + colorUniforms } type blitTexUniforms struct { - vert struct { - blitUniforms - _ [12]byte // Padding to a multiple of 16. - } + blitUniforms +} + +type blitLinearGradientUniforms struct { + blitUniforms + _ [128 - unsafe.Sizeof(blitUniforms{}) - unsafe.Sizeof(gradientUniforms{})]byte // Padding to 128 bytes. + gradientUniforms } type uniformBuffer struct { - buf backend.Buffer + buf driver.Buffer ptr []byte } -type program struct { - prog backend.Program - vertUniforms *uniformBuffer - fragUniforms *uniformBuffer +type pipeline struct { + pipeline driver.Pipeline + uniforms *uniformBuffer } type blitUniforms struct { transform [4]float32 uvTransformR1 [4]float32 uvTransformR2 [4]float32 - z float32 } type colorUniforms struct { color f32color.RGBA } +type gradientUniforms struct { + color1 f32color.RGBA + color2 f32color.RGBA +} + type materialType uint8 const ( @@ -273,14 +292,37 @@ const ( const ( materialColor materialType = iota + materialLinearGradient materialTexture ) -func New(ctx backend.Device) (*GPU, error) { - defFBO := ctx.CurrentFramebuffer() - g := &GPU{ - defFBO: defFBO, - cache: newResourceCache(), +// New creates a GPU for the given API. +func New(api API) (GPU, error) { + d, err := driver.NewDevice(api) + if err != nil { + return nil, err + } + return NewWithDevice(d) +} + +// NewWithDevice creates a GPU with a pre-existing device. +// +// Note: for internal use only. +func NewWithDevice(d driver.Device) (GPU, error) { + d.BeginFrame(nil, false, image.Point{}) + defer d.EndFrame() + forceCompute := os.Getenv("GIORENDERER") == "forcecompute" + feats := d.Caps().Features + switch { + case !forceCompute && feats.Has(driver.FeatureFloatRenderTargets) && feats.Has(driver.FeatureSRGB): + return newGPU(d) + } + return newCompute(d) +} + +func newGPU(ctx driver.Device) (*gpu, error) { + g := &gpu{ + cache: newResourceCache(), } g.drawOps.pathCache = newOpCache() if err := g.init(ctx); err != nil { @@ -289,111 +331,116 @@ func New(ctx backend.Device) (*GPU, error) { return g, nil } -func (g *GPU) init(ctx backend.Device) error { +func (g *gpu) init(ctx driver.Device) error { g.ctx = ctx g.renderer = newRenderer(ctx) return nil } -func (g *GPU) Release() { +func (g *gpu) Clear(col color.NRGBA) { + g.drawOps.clear = true + g.drawOps.clearColor = f32color.LinearFromSRGB(col) +} + +func (g *gpu) Release() { g.renderer.release() g.drawOps.pathCache.release() g.cache.release() if g.timers != nil { - g.timers.release() + g.timers.Release() } + g.ctx.Release() +} + +func (g *gpu) Frame(frameOps *op.Ops, target RenderTarget, viewport image.Point) error { + g.collect(viewport, frameOps) + return g.frame(target) } -func (g *GPU) Collect(viewport image.Point, frameOps *op.Ops) { +func (g *gpu) collect(viewport image.Point, frameOps *op.Ops) { g.renderer.blitter.viewport = viewport g.renderer.pather.viewport = viewport - g.drawOps.reset(g.cache, viewport) - g.drawOps.collect(g.cache, frameOps, viewport) + g.drawOps.reset(viewport) + g.drawOps.collect(frameOps, viewport) g.frameStart = time.Now() - if g.drawOps.profile && g.timers == nil && g.ctx.Caps().Features.Has(backend.FeatureTimers) { + if g.drawOps.profile && g.timers == nil && g.ctx.Caps().Features.Has(driver.FeatureTimers) { g.timers = newTimers(g.ctx) - g.zopsTimer = g.timers.newTimer() g.stencilTimer = g.timers.newTimer() g.coverTimer = g.timers.newTimer() g.cleanupTimer = g.timers.newTimer() } - for _, p := range g.drawOps.pathOps { - if v, exists := g.drawOps.pathCache.get(p.pathKey); !exists || v.data.data == nil { - data := buildPath(g.ctx, p.pathVerts) - g.drawOps.pathCache.put(p.pathKey, opCacheValue{ - data: data, - bounds: p.bounds, - }) - } - p.pathVerts = nil - } } -func (g *GPU) BeginFrame() { - g.ctx.BeginFrame() - defer g.ctx.EndFrame() +func (g *gpu) frame(target RenderTarget) error { viewport := g.renderer.blitter.viewport + defFBO := g.ctx.BeginFrame(target, g.drawOps.clear, viewport) + defer g.ctx.EndFrame() + g.drawOps.buildPaths(g.ctx) for _, img := range g.drawOps.imageOps { expandPathOp(img.path, img.clip) } - if g.drawOps.profile { - g.zopsTimer.begin() - } - g.ctx.BindFramebuffer(g.defFBO) - g.ctx.DepthFunc(backend.DepthFuncGreater) - g.ctx.ClearDepth(0.0) - g.ctx.Clear(g.drawOps.clearColor.Float32()) - g.ctx.Viewport(0, 0, viewport.X, viewport.Y) - g.renderer.drawZOps(g.drawOps.zimageOps) - g.zopsTimer.end() g.stencilTimer.begin() - g.ctx.SetBlend(true) g.renderer.packStencils(&g.drawOps.pathOps) g.renderer.stencilClips(g.drawOps.pathCache, g.drawOps.pathOps) g.renderer.packIntersections(g.drawOps.imageOps) + g.renderer.prepareIntersections(g.drawOps.imageOps) g.renderer.intersect(g.drawOps.imageOps) g.stencilTimer.end() g.coverTimer.begin() - g.ctx.BindFramebuffer(g.defFBO) + g.renderer.uploadImages(g.cache, g.drawOps.imageOps) + g.renderer.prepareDrawOps(g.cache, g.drawOps.imageOps) + d := driver.LoadDesc{ + ClearColor: g.drawOps.clearColor, + } + if g.drawOps.clear { + g.drawOps.clear = false + d.Action = driver.LoadActionClear + } + g.ctx.BeginRenderPass(defFBO, d) g.ctx.Viewport(0, 0, viewport.X, viewport.Y) - g.renderer.drawOps(g.drawOps.imageOps) - g.ctx.SetBlend(false) - g.renderer.pather.stenciler.invalidateFBO() + g.renderer.drawOps(g.cache, g.drawOps.imageOps) g.coverTimer.end() - g.ctx.BindFramebuffer(g.defFBO) -} - -func (g *GPU) EndFrame() { + g.ctx.EndRenderPass() g.cleanupTimer.begin() g.cache.frame() g.drawOps.pathCache.frame() g.cleanupTimer.end() if g.drawOps.profile && g.timers.ready() { - zt, st, covt, cleant := g.zopsTimer.Elapsed, g.stencilTimer.Elapsed, g.coverTimer.Elapsed, g.cleanupTimer.Elapsed - ft := zt + st + covt + cleant + st, covt, cleant := g.stencilTimer.Elapsed, g.coverTimer.Elapsed, g.cleanupTimer.Elapsed + ft := st + covt + cleant q := 100 * time.Microsecond - zt, st, covt = zt.Round(q), st.Round(q), covt.Round(q) + st, covt = st.Round(q), covt.Round(q) frameDur := time.Since(g.frameStart).Round(q) ft = ft.Round(q) - g.profile = fmt.Sprintf("draw:%7s gpu:%7s zt:%7s st:%7s cov:%7s", frameDur, ft, zt, st, covt) + g.profile = fmt.Sprintf("draw:%7s gpu:%7s st:%7s cov:%7s", frameDur, ft, st, covt) } + return nil } -func (g *GPU) Profile() string { +func (g *gpu) Profile() string { return g.profile } -func (r *renderer) texHandle(t *texture) backend.Texture { - if t.tex != nil { - return t.tex +func (r *renderer) texHandle(cache *resourceCache, data imageOpData) driver.Texture { + var tex *texture + t, exists := cache.get(data.handle) + if !exists { + t = &texture{ + src: data.src, + } + cache.put(data.handle, t) } - tex, err := r.ctx.NewTexture(backend.TextureFormatSRGB, t.src.Bounds().Dx(), t.src.Bounds().Dy(), backend.FilterLinear, backend.FilterLinear, backend.BufferBindingTexture) + tex = t.(*texture) + if tex.tex != nil { + return tex.tex + } + handle, err := r.ctx.NewTexture(driver.TextureFormatSRGBA, data.src.Bounds().Dx(), data.src.Bounds().Dy(), driver.FilterLinear, driver.FilterLinear, driver.BufferBindingTexture) if err != nil { panic(err) } - tex.Upload(t.src) - t.tex = tex - return t.tex + driver.UploadImage(handle, image.Pt(0, 0), data.src) + tex.tex = handle + return tex.tex } func (t *texture) release() { @@ -402,14 +449,22 @@ func (t *texture) release() { } } -func newRenderer(ctx backend.Device) *renderer { +func newRenderer(ctx driver.Device) *renderer { r := &renderer{ ctx: ctx, blitter: newBlitter(ctx), pather: newPather(ctx), } - r.packer.maxDim = ctx.Caps().MaxTextureSize - r.intersections.maxDim = r.packer.maxDim + + maxDim := ctx.Caps().MaxTextureSize + // Large atlas textures cause artifacts due to precision loss in + // shaders. + if cap := 8192; maxDim > cap { + maxDim = cap + } + + r.packer.maxDims = image.Pt(maxDim, maxDim) + r.intersections.maxDims = image.Pt(maxDim, maxDim) return r } @@ -418,13 +473,13 @@ func (r *renderer) release() { r.blitter.release() } -func newBlitter(ctx backend.Device) *blitter { - quadVerts, err := ctx.NewImmutableBuffer(backend.BufferBindingVertices, - gunsafe.BytesView([]float32{ - -1, +1, 0, 0, - +1, +1, 1, 0, - -1, -1, 0, 1, - +1, -1, 1, 1, +func newBlitter(ctx driver.Device) *blitter { + quadVerts, err := ctx.NewImmutableBuffer(driver.BufferBindingVertices, + byteslice.Slice([]float32{ + -1, -1, 0, 0, + +1, -1, 1, 0, + -1, +1, 0, 1, + +1, +1, 1, 1, }), ) if err != nil { @@ -436,65 +491,125 @@ func newBlitter(ctx backend.Device) *blitter { } b.colUniforms = new(blitColUniforms) b.texUniforms = new(blitTexUniforms) - prog, layout, err := createColorPrograms(ctx, shader_blit_vert, shader_blit_frag, - [2]interface{}{&b.colUniforms.vert, &b.texUniforms.vert}, [2]interface{}{&b.colUniforms.frag, nil}) + b.linearGradientUniforms = new(blitLinearGradientUniforms) + pipelines, err := createColorPrograms(ctx, gio.Shader_blit_vert, gio.Shader_blit_frag, + [3]interface{}{b.colUniforms, b.linearGradientUniforms, b.texUniforms}, + ) if err != nil { panic(err) } - b.prog = prog - b.layout = layout + b.pipelines = pipelines return b } func (b *blitter) release() { b.quadVerts.Release() - for _, p := range b.prog { + for _, p := range b.pipelines { p.Release() } - b.layout.Release() } -func createColorPrograms(b backend.Device, vsSrc backend.ShaderSources, fsSrc [2]backend.ShaderSources, vertUniforms, fragUniforms [2]interface{}) ([2]*program, backend.InputLayout, error) { - var progs [2]*program - prog, err := b.NewProgram(vsSrc, fsSrc[materialTexture]) - if err != nil { - return progs, nil, err +func createColorPrograms(b driver.Device, vsSrc shader.Sources, fsSrc [3]shader.Sources, uniforms [3]interface{}) ([3]*pipeline, error) { + var pipelines [3]*pipeline + blend := driver.BlendDesc{ + Enable: true, + SrcFactor: driver.BlendFactorOne, + DstFactor: driver.BlendFactorOneMinusSrcAlpha, } - var vertBuffer *uniformBuffer - if u := vertUniforms[materialTexture]; u != nil { - vertBuffer = newUniformBuffer(b, u) - prog.SetVertexUniforms(vertBuffer.buf) - } - var fragBuffer *uniformBuffer - if u := fragUniforms[materialTexture]; u != nil { - fragBuffer = newUniformBuffer(b, u) - prog.SetFragmentUniforms(fragBuffer.buf) + layout := driver.VertexLayout{ + Inputs: []driver.InputDesc{ + {Type: shader.DataTypeFloat, Size: 2, Offset: 0}, + {Type: shader.DataTypeFloat, Size: 2, Offset: 4 * 2}, + }, + Stride: 4 * 4, } - progs[materialTexture] = newProgram(prog, vertBuffer, fragBuffer) - prog, err = b.NewProgram(vsSrc, fsSrc[materialColor]) + vsh, err := b.NewVertexShader(vsSrc) if err != nil { - progs[materialTexture].Release() - return progs, nil, err - } - if u := vertUniforms[materialColor]; u != nil { - vertBuffer = newUniformBuffer(b, u) - prog.SetVertexUniforms(vertBuffer.buf) - } - if u := fragUniforms[materialColor]; u != nil { - fragBuffer = newUniformBuffer(b, u) - prog.SetFragmentUniforms(fragBuffer.buf) - } - progs[materialColor] = newProgram(prog, vertBuffer, fragBuffer) - layout, err := b.NewInputLayout(vsSrc, []backend.InputDesc{ - {Type: backend.DataTypeFloat, Size: 2, Offset: 0}, - {Type: backend.DataTypeFloat, Size: 2, Offset: 4 * 2}, - }) + return pipelines, err + } + defer vsh.Release() + { + fsh, err := b.NewFragmentShader(fsSrc[materialTexture]) + if err != nil { + return pipelines, err + } + defer fsh.Release() + pipe, err := b.NewPipeline(driver.PipelineDesc{ + VertexShader: vsh, + FragmentShader: fsh, + BlendDesc: blend, + VertexLayout: layout, + PixelFormat: driver.TextureFormatOutput, + Topology: driver.TopologyTriangleStrip, + }) + if err != nil { + return pipelines, err + } + var vertBuffer *uniformBuffer + if u := uniforms[materialTexture]; u != nil { + vertBuffer = newUniformBuffer(b, u) + } + pipelines[materialTexture] = &pipeline{pipe, vertBuffer} + } + { + var vertBuffer *uniformBuffer + fsh, err := b.NewFragmentShader(fsSrc[materialColor]) + if err != nil { + pipelines[materialTexture].Release() + return pipelines, err + } + defer fsh.Release() + pipe, err := b.NewPipeline(driver.PipelineDesc{ + VertexShader: vsh, + FragmentShader: fsh, + BlendDesc: blend, + VertexLayout: layout, + PixelFormat: driver.TextureFormatOutput, + Topology: driver.TopologyTriangleStrip, + }) + if err != nil { + pipelines[materialTexture].Release() + return pipelines, err + } + if u := uniforms[materialColor]; u != nil { + vertBuffer = newUniformBuffer(b, u) + } + pipelines[materialColor] = &pipeline{pipe, vertBuffer} + } + { + var vertBuffer *uniformBuffer + fsh, err := b.NewFragmentShader(fsSrc[materialLinearGradient]) + if err != nil { + pipelines[materialTexture].Release() + pipelines[materialColor].Release() + return pipelines, err + } + defer fsh.Release() + pipe, err := b.NewPipeline(driver.PipelineDesc{ + VertexShader: vsh, + FragmentShader: fsh, + BlendDesc: blend, + VertexLayout: layout, + PixelFormat: driver.TextureFormatOutput, + Topology: driver.TopologyTriangleStrip, + }) + if err != nil { + pipelines[materialTexture].Release() + pipelines[materialColor].Release() + return pipelines, err + } + if u := uniforms[materialLinearGradient]; u != nil { + vertBuffer = newUniformBuffer(b, u) + } + pipelines[materialLinearGradient] = &pipeline{pipe, vertBuffer} + } if err != nil { - progs[materialTexture].Release() - progs[materialColor].Release() - return progs, nil, err + for _, p := range pipelines { + p.Release() + } + return pipelines, err } - return progs, layout, nil + return pipelines, nil } func (r *renderer) stencilClips(pathCache *opCache, ops []*pathOp) { @@ -505,14 +620,31 @@ func (r *renderer) stencilClips(pathCache *opCache, ops []*pathOp) { r.pather.begin(r.packer.sizes) for _, p := range ops { if fbo != p.place.Idx { + if fbo != -1 { + r.ctx.EndRenderPass() + } fbo = p.place.Idx f := r.pather.stenciler.cover(fbo) - r.ctx.BindFramebuffer(f.fbo) - r.ctx.Clear(0.0, 0.0, 0.0, 0.0) + r.ctx.BeginRenderPass(f.tex, driver.LoadDesc{Action: driver.LoadActionClear}) + r.ctx.BindPipeline(r.pather.stenciler.pipeline.pipeline.pipeline) + r.ctx.BindIndexBuffer(r.pather.stenciler.indexBuf) } v, _ := pathCache.get(p.pathKey) r.pather.stencilPath(p.clip, p.off, p.place.Pos, v.data) } + if fbo != -1 { + r.ctx.EndRenderPass() + } +} + +func (r *renderer) prepareIntersections(ops []imageOp) { + for _, img := range ops { + if img.clipType != clipTypeIntersection { + continue + } + fbo := r.pather.stenciler.cover(img.path.place.Idx) + r.ctx.PrepareTexture(fbo.tex) + } } func (r *renderer) intersect(ops []imageOp) { @@ -521,21 +653,28 @@ func (r *renderer) intersect(ops []imageOp) { } fbo := -1 r.pather.stenciler.beginIntersect(r.intersections.sizes) - r.ctx.BindVertexBuffer(r.blitter.quadVerts, 4*4, 0) - r.ctx.BindInputLayout(r.pather.stenciler.iprog.layout) for _, img := range ops { if img.clipType != clipTypeIntersection { continue } if fbo != img.place.Idx { + if fbo != -1 { + r.ctx.EndRenderPass() + } fbo = img.place.Idx f := r.pather.stenciler.intersections.fbos[fbo] - r.ctx.BindFramebuffer(f.fbo) - r.ctx.Clear(1.0, 0.0, 0.0, 0.0) + d := driver.LoadDesc{Action: driver.LoadActionClear} + d.ClearColor.R = 1.0 + r.ctx.BeginRenderPass(f.tex, d) + r.ctx.BindPipeline(r.pather.stenciler.ipipeline.pipeline.pipeline) + r.ctx.BindVertexBuffer(r.blitter.quadVerts, 0) } r.ctx.Viewport(img.place.Pos.X, img.place.Pos.Y, img.clip.Dx(), img.clip.Dy()) r.intersectPath(img.path, img.clip) } + if fbo != -1 { + r.ctx.EndRenderPass() + } } func (r *renderer) intersectPath(p *pathOp, clip image.Rectangle) { @@ -556,12 +695,12 @@ func (r *renderer) intersectPath(p *pathOp, clip image.Rectangle) { } fbo := r.pather.stenciler.cover(p.place.Idx) r.ctx.BindTexture(0, fbo.tex) - coverScale, coverOff := texSpaceTransform(toRectF(uv), fbo.size) - subScale, subOff := texSpaceTransform(toRectF(sub), p.clip.Size()) - r.pather.stenciler.iprog.uniforms.vert.uvTransform = [4]float32{coverScale.X, coverScale.Y, coverOff.X, coverOff.Y} - r.pather.stenciler.iprog.uniforms.vert.subUVTransform = [4]float32{subScale.X, subScale.Y, subOff.X, subOff.Y} - r.pather.stenciler.iprog.prog.UploadUniforms() - r.ctx.DrawArrays(backend.DrawModeTriangleStrip, 0, 4) + coverScale, coverOff := texSpaceTransform(layout.FRect(uv), fbo.size) + subScale, subOff := texSpaceTransform(layout.FRect(sub), p.clip.Size()) + r.pather.stenciler.ipipeline.uniforms.vert.uvTransform = [4]float32{coverScale.X, coverScale.Y, coverOff.X, coverOff.Y} + r.pather.stenciler.ipipeline.uniforms.vert.subUVTransform = [4]float32{subScale.X, subScale.Y, subOff.X, subOff.Y} + r.pather.stenciler.ipipeline.pipeline.UploadUniforms(r.ctx) + r.ctx.DrawArrays(0, 4) } func (r *renderer) packIntersections(ops []imageOp) { @@ -611,7 +750,7 @@ func (r *renderer) packStencils(pops *[]*pathOp) { if !ok { // The clip area is at most the entire screen. Hopefully no // screen is larger than GL_MAX_TEXTURE_SIZE. - panic(fmt.Errorf("clip area %v is larger than maximum texture size %dx%d", p.clip, r.packer.maxDim, r.packer.maxDim)) + panic(fmt.Errorf("clip area %v is larger than maximum texture size %v", p.clip, r.packer.maxDims)) } p.place = place i++ @@ -619,8 +758,7 @@ func (r *renderer) packStencils(pops *[]*pathOp) { *pops = ops } -// intersects intersects clip and b where b is offset by off. -// ceilRect returns a bounding image.Rectangle for a f32.Rectangle. +// boundRectF returns a bounding image.Rectangle for a f32.Rectangle. func boundRectF(r f32.Rectangle) image.Rectangle { return image.Rectangle{ Min: image.Point{ @@ -634,19 +772,6 @@ func boundRectF(r f32.Rectangle) image.Rectangle { } } -func toRectF(r image.Rectangle) f32.Rectangle { - return f32.Rectangle{ - Min: f32.Point{ - X: float32(r.Min.X), - Y: float32(r.Min.Y), - }, - Max: f32.Point{ - X: float32(r.Max.X), - Y: float32(r.Max.Y), - }, - } -} - func ceil(v float32) int { return int(math.Ceil(float64(v))) } @@ -655,30 +780,39 @@ func floor(v float32) int { return int(math.Floor(float64(v))) } -func (d *drawOps) reset(cache *resourceCache, viewport image.Point) { +func (d *drawOps) reset(viewport image.Point) { d.profile = false - d.clearColor = f32color.RGBA{R: 1.0, G: 1.0, B: 1.0, A: 1.0} - d.cache = cache d.viewport = viewport d.imageOps = d.imageOps[:0] - d.zimageOps = d.zimageOps[:0] d.pathOps = d.pathOps[:0] d.pathOpCache = d.pathOpCache[:0] d.vertCache = d.vertCache[:0] + d.transStack = d.transStack[:0] } -func (d *drawOps) collect(cache *resourceCache, root *op.Ops, viewport image.Point) { - d.reset(cache, viewport) - clip := f32.Rectangle{ +func (d *drawOps) collect(root *op.Ops, viewport image.Point) { + viewf := f32.Rectangle{ Max: f32.Point{X: float32(viewport.X), Y: float32(viewport.Y)}, } - d.reader.Reset(root) - state := drawState{ - clip: clip, - rect: true, - color: color.RGBA{A: 0xff}, + var ops *ops.Ops + if root != nil { + ops = &root.Internal + } + d.reader.Reset(ops) + d.collectOps(&d.reader, viewf) +} + +func (d *drawOps) buildPaths(ctx driver.Device) { + for _, p := range d.pathOps { + if v, exists := d.pathCache.get(p.pathKey); !exists || v.data.data == nil { + data := buildPath(ctx, p.pathVerts) + d.pathCache.put(p.pathKey, opCacheValue{ + data: data, + bounds: p.bounds, + }) + } + p.pathVerts = nil } - d.collectOps(&d.reader, state) } func (d *drawOps) newPathOp() *pathOp { @@ -686,24 +820,30 @@ func (d *drawOps) newPathOp() *pathOp { return &d.pathOpCache[len(d.pathOpCache)-1] } -func (d *drawOps) addClipPath(state *drawState, aux []byte, auxKey ops.Key, bounds f32.Rectangle, off f32.Point) { +func (d *drawOps) addClipPath(state *drawState, aux []byte, auxKey opKey, bounds f32.Rectangle, off f32.Point, push bool) { npath := d.newPathOp() *npath = pathOp{ - parent: state.cpath, - bounds: bounds, - off: off, + parent: state.cpath, + bounds: bounds, + off: off, + intersect: bounds.Add(off), + rect: true, + } + if npath.parent != nil { + npath.rect = npath.parent.rect + npath.intersect = npath.parent.intersect.Intersect(npath.intersect) } - state.cpath = npath if len(aux) > 0 { - state.rect = false - state.cpath.pathKey = auxKey - state.cpath.path = true - state.cpath.pathVerts = aux - d.pathOps = append(d.pathOps, state.cpath) + npath.rect = false + npath.pathKey = auxKey + npath.path = true + npath.pathVerts = aux + d.pathOps = append(d.pathOps, npath) } + state.cpath = npath } -// split a transform into two parts, one which is pur offset and the +// split a transform into two parts, one which is pure offset and the // other representing the scaling, shearing and rotation part func splitTransform(t f32.Affine2D) (srs f32.Affine2D, offset f32.Point) { sx, hx, ox, hy, sy, oy := t.Elems() @@ -712,121 +852,169 @@ func splitTransform(t f32.Affine2D) (srs f32.Affine2D, offset f32.Point) { return } -func (d *drawOps) collectOps(r *ops.Reader, state drawState) int { - var aux []byte - var auxKey ops.Key +func (d *drawOps) save(id int, state f32.Affine2D) { + if extra := id - len(d.states) + 1; extra > 0 { + d.states = append(d.states, make([]f32.Affine2D, extra)...) + } + d.states[id] = state +} + +func (k opKey) SetTransform(t f32.Affine2D) opKey { + sx, hx, _, hy, sy, _ := t.Elems() + k.sx = sx + k.hx = hx + k.hy = hy + k.sy = sy + return k +} + +func (d *drawOps) collectOps(r *ops.Reader, viewport f32.Rectangle) { + var ( + quads quadsOp + state drawState + ) + reset := func() { + state = drawState{ + color: color.NRGBA{A: 0xff}, + } + } + reset() loop: for encOp, ok := r.Decode(); ok; encOp, ok = r.Decode() { - switch opconst.OpType(encOp.Data[0]) { - case opconst.TypeProfile: + switch ops.OpType(encOp.Data[0]) { + case ops.TypeProfile: d.profile = true - case opconst.TypeTransform: - dop := ops.DecodeTransform(encOp.Data) + case ops.TypeTransform: + dop, push := ops.DecodeTransform(encOp.Data) + if push { + d.transStack = append(d.transStack, state.t) + } state.t = state.t.Mul(dop) - case opconst.TypeAux: - aux = encOp.Data[opconst.TypeAuxLen:] - auxKey = encOp.Key - case opconst.TypeClip: - var op clipOp - op.decode(encOp.Data) - bounds := op.bounds + case ops.TypePopTransform: + n := len(d.transStack) + state.t = d.transStack[n-1] + d.transStack = d.transStack[:n-1] + + case ops.TypeStroke: + quads.key.strokeWidth = decodeStrokeOp(encOp.Data) + + case ops.TypePath: + encOp, ok = r.Decode() + if !ok { + break loop + } + quads.aux = encOp.Data[ops.TypeAuxLen:] + quads.key.Key = encOp.Key + + case ops.TypeClip: + var op ops.ClipOp + op.Decode(encOp.Data) + quads.key.outline = op.Outline + bounds := layout.FRect(op.Bounds) trans, off := splitTransform(state.t) - if len(aux) > 0 { + if len(quads.aux) > 0 { // There is a clipping path, build the gpu data and update the // cache key such that it will be equal only if the transform is the // same also. Use cached data if we have it. - auxKey = auxKey.SetTransform(trans) - if v, ok := d.pathCache.get(auxKey); ok { + quads.key = quads.key.SetTransform(trans) + if v, ok := d.pathCache.get(quads.key); ok { // Since the GPU data exists in the cache aux will not be used. // Why is this not used for the offset shapes? - op.bounds = v.bounds + bounds = v.bounds } else { - aux, op.bounds = d.buildVerts(aux, trans) + var pathData []byte + pathData, bounds = d.buildVerts( + quads.aux, trans, quads.key.outline, quads.key.strokeWidth, + ) + quads.aux = pathData // add it to the cache, without GPU data, so the transform can be // reused. - d.pathCache.put(auxKey, opCacheValue{bounds: op.bounds}) + d.pathCache.put(quads.key, opCacheValue{bounds: bounds}) } } else { - aux, op.bounds, _ = d.boundsForTransformedRect(bounds, trans) - auxKey = encOp.Key - auxKey.SetTransform(trans) + quads.aux, bounds, _ = d.boundsForTransformedRect(bounds, trans) + quads.key = opKey{Key: encOp.Key} } - state.clip = state.clip.Intersect(op.bounds.Add(off)) - d.addClipPath(&state, aux, auxKey, op.bounds, off) - aux = nil - auxKey = ops.Key{} - case opconst.TypeColor: + d.addClipPath(&state, quads.aux, quads.key, bounds, off, true) + quads = quadsOp{} + case ops.TypePopClip: + state.cpath = state.cpath.parent + + case ops.TypeColor: state.matType = materialColor state.color = decodeColorOp(encOp.Data) - case opconst.TypeImage: + case ops.TypeLinearGradient: + state.matType = materialLinearGradient + op := decodeLinearGradientOp(encOp.Data) + state.stop1 = op.stop1 + state.stop2 = op.stop2 + state.color1 = op.color1 + state.color2 = op.color2 + case ops.TypeImage: state.matType = materialTexture state.image = decodeImageOp(encOp.Data, encOp.Refs) - case opconst.TypePaint: - op := decodePaintOp(encOp.Data) + case ops.TypePaint: // Transform (if needed) the painting rectangle and if so generate a clip path, // for those cases also compute a partialTrans that maps texture coordinates between // the new bounding rectangle and the transformed original paint rectangle. - trans, off := splitTransform(state.t) - clipData, bnd, partialTrans := d.boundsForTransformedRect(op.Rect, trans) - clip := state.clip.Intersect(bnd.Add(off)) - if clip.Empty() { + t, off := splitTransform(state.t) + // Fill the clip area, unless the material is a (bounded) image. + // TODO: Find a tighter bound. + inf := float32(1e6) + dst := f32.Rect(-inf, -inf, inf, inf) + if state.matType == materialTexture { + sz := state.image.src.Rect.Size() + dst = f32.Rectangle{Max: layout.FPt(sz)} + } + clipData, bnd, partialTrans := d.boundsForTransformedRect(dst, t) + cl := viewport.Intersect(bnd.Add(off)) + if state.cpath != nil { + cl = state.cpath.intersect.Intersect(cl) + } + if cl.Empty() { continue } - wasrect := state.rect if clipData != nil { // The paint operation is sheared or rotated, add a clip path representing // this transformed rectangle. - encOp.Key.SetTransform(trans) - d.addClipPath(&state, clipData, encOp.Key, bnd, off) + k := opKey{Key: encOp.Key} + k.SetTransform(t) // TODO: This call has no effect. + d.addClipPath(&state, clipData, k, bnd, off, false) } - bounds := boundRectF(clip) - mat := state.materialFor(d.cache, bnd, off, partialTrans, bounds) + bounds := boundRectF(cl) + mat := state.materialFor(bnd, off, partialTrans, bounds) - if bounds.Min == (image.Point{}) && bounds.Max == d.viewport && state.rect && mat.opaque && mat.material == materialColor { + rect := state.cpath == nil || state.cpath.rect + if bounds.Min == (image.Point{}) && bounds.Max == d.viewport && rect && mat.opaque && (mat.material == materialColor) { // The image is a uniform opaque color and takes up the whole screen. // Scrap images up to and including this image and set clear color. - d.zimageOps = d.zimageOps[:0] d.imageOps = d.imageOps[:0] - state.z = 0 d.clearColor = mat.color.Opaque() + d.clear = true continue } - state.z++ - if state.z != int(uint16(state.z)) { - // TODO(eliasnaur) gioui.org/issue/127. - panic("more than 65k paint objects not supported") - } - // Assume 16-bit depth buffer. - const zdepth = 1 << 16 - // Convert z to window-space, assuming depth range [0;1]. - zf := float32(state.z)*2/zdepth - 1.0 img := imageOp{ - z: zf, path: state.cpath, - off: off, clip: bounds, material: mat, } - if state.rect && img.material.opaque { - d.zimageOps = append(d.zimageOps, img) - } else { - d.imageOps = append(d.imageOps, img) - } + d.imageOps = append(d.imageOps, img) if clipData != nil { // we added a clip path that should not remain state.cpath = state.cpath.parent - state.rect = wasrect } - case opconst.TypePush: - state.z = d.collectOps(r, state) - case opconst.TypePop: - break loop + case ops.TypeSave: + id := ops.DecodeSave(encOp.Data) + d.save(id, state.t) + case ops.TypeLoad: + reset() + id := ops.DecodeLoad(encOp.Data) + state.t = d.states[id] } } - return state.z } func expandPathOp(p *pathOp, clip image.Rectangle) { @@ -840,78 +1028,83 @@ func expandPathOp(p *pathOp, clip image.Rectangle) { } } -func (d *drawState) materialFor(cache *resourceCache, rect f32.Rectangle, off f32.Point, trans f32.Affine2D, clip image.Rectangle) material { +func (d *drawState) materialFor(rect f32.Rectangle, off f32.Point, partTrans f32.Affine2D, clip image.Rectangle) material { var m material switch d.matType { case materialColor: m.material = materialColor - m.color = f32color.RGBAFromSRGB(d.color) + m.color = f32color.LinearFromSRGB(d.color) m.opaque = m.color.A == 1.0 + case materialLinearGradient: + m.material = materialLinearGradient + + m.color1 = f32color.LinearFromSRGB(d.color1) + m.color2 = f32color.LinearFromSRGB(d.color2) + m.opaque = m.color1.A == 1.0 && m.color2.A == 1.0 + + m.uvTrans = partTrans.Mul(gradientSpaceTransform(clip, off, d.stop1, d.stop2)) case materialTexture: m.material = materialTexture dr := boundRectF(rect.Add(off)) sz := d.image.src.Bounds().Size() - sr := layout.FRect(d.image.rect) - if dx := float32(dr.Dx()); dx != 0 { - // Don't clip 1 px width sources. - if sdx := sr.Dx(); sdx > 1 { - sr.Min.X += (float32(clip.Min.X-dr.Min.X)*sdx + dx/2) / dx - sr.Max.X -= (float32(dr.Max.X-clip.Max.X)*sdx + dx/2) / dx - } - } - if dy := float32(dr.Dy()); dy != 0 { - // Don't clip 1 px height sources. - if sdy := sr.Dy(); sdy > 1 { - sr.Min.Y += (float32(clip.Min.Y-dr.Min.Y)*sdy + dy/2) / dy - sr.Max.Y -= (float32(dr.Max.Y-clip.Max.Y)*sdy + dy/2) / dy - } - } - tex, exists := cache.get(d.image.handle) - if !exists { - t := &texture{ - src: d.image.src, - } - cache.put(d.image.handle, t) - tex = t + sr := f32.Rectangle{ + Max: f32.Point{ + X: float32(sz.X), + Y: float32(sz.Y), + }, } - m.texture = tex.(*texture) + dx := float32(dr.Dx()) + sdx := sr.Dx() + sr.Min.X += float32(clip.Min.X-dr.Min.X) * sdx / dx + sr.Max.X -= float32(dr.Max.X-clip.Max.X) * sdx / dx + dy := float32(dr.Dy()) + sdy := sr.Dy() + sr.Min.Y += float32(clip.Min.Y-dr.Min.Y) * sdy / dy + sr.Max.Y -= float32(dr.Max.Y-clip.Max.Y) * sdy / dy uvScale, uvOffset := texSpaceTransform(sr, sz) - m.uvTrans = trans.Mul(f32.Affine2D{}.Scale(f32.Point{}, uvScale).Offset(uvOffset)) + m.uvTrans = partTrans.Mul(f32.Affine2D{}.Scale(f32.Point{}, uvScale).Offset(uvOffset)) + m.data = d.image } return m } -func (r *renderer) drawZOps(ops []imageOp) { - r.ctx.SetDepthTest(true) - r.ctx.BindVertexBuffer(r.blitter.quadVerts, 4*4, 0) - r.ctx.BindInputLayout(r.blitter.layout) - // Render front to back. - for i := len(ops) - 1; i >= 0; i-- { - img := ops[i] +func (r *renderer) uploadImages(cache *resourceCache, ops []imageOp) { + for _, img := range ops { + m := img.material + if m.material == materialTexture { + r.texHandle(cache, m.data) + } + } +} + +func (r *renderer) prepareDrawOps(cache *resourceCache, ops []imageOp) { + for _, img := range ops { m := img.material switch m.material { case materialTexture: - r.ctx.BindTexture(0, r.texHandle(m.texture)) + r.ctx.PrepareTexture(r.texHandle(cache, m.data)) } - drc := img.clip - scale, off := clipSpaceTransform(drc, r.blitter.viewport) - r.blitter.blit(img.z, m.material, m.color, scale, off, m.uvTrans) + + var fbo stencilFBO + switch img.clipType { + case clipTypeNone: + continue + case clipTypePath: + fbo = r.pather.stenciler.cover(img.place.Idx) + case clipTypeIntersection: + fbo = r.pather.stenciler.intersections.fbos[img.place.Idx] + } + r.ctx.PrepareTexture(fbo.tex) } - r.ctx.SetDepthTest(false) } -func (r *renderer) drawOps(ops []imageOp) { - r.ctx.SetDepthTest(true) - r.ctx.DepthMask(false) - r.ctx.BlendFunc(backend.BlendFactorOne, backend.BlendFactorOneMinusSrcAlpha) - r.ctx.BindVertexBuffer(r.blitter.quadVerts, 4*4, 0) - r.ctx.BindInputLayout(r.pather.coverer.layout) - var coverTex backend.Texture +func (r *renderer) drawOps(cache *resourceCache, ops []imageOp) { + var coverTex driver.Texture for _, img := range ops { m := img.material switch m.material { case materialTexture: - r.ctx.BindTexture(0, r.texHandle(m.texture)) + r.ctx.BindTexture(0, r.texHandle(cache, m.data)) } drc := img.clip @@ -919,7 +1112,10 @@ func (r *renderer) drawOps(ops []imageOp) { var fbo stencilFBO switch img.clipType { case clipTypeNone: - r.blitter.blit(img.z, m.material, m.color, scale, off, m.uvTrans) + p := r.blitter.pipelines[m.material] + r.ctx.BindPipeline(p.pipeline) + r.ctx.BindVertexBuffer(r.blitter.quadVerts, 0) + r.blitter.blit(m.material, m.color, m.color1, m.color2, scale, off, m.uvTrans) continue case clipTypePath: fbo = r.pather.stenciler.cover(img.place.Idx) @@ -934,42 +1130,50 @@ func (r *renderer) drawOps(ops []imageOp) { Min: img.place.Pos, Max: img.place.Pos.Add(drc.Size()), } - coverScale, coverOff := texSpaceTransform(toRectF(uv), fbo.size) - r.pather.cover(img.z, m.material, m.color, scale, off, m.uvTrans, coverScale, coverOff) + coverScale, coverOff := texSpaceTransform(layout.FRect(uv), fbo.size) + p := r.pather.coverer.pipelines[m.material] + r.ctx.BindPipeline(p.pipeline) + r.ctx.BindVertexBuffer(r.blitter.quadVerts, 0) + r.pather.cover(m.material, m.color, m.color1, m.color2, scale, off, m.uvTrans, coverScale, coverOff) } - r.ctx.DepthMask(true) - r.ctx.SetDepthTest(false) } -func (b *blitter) blit(z float32, mat materialType, col f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D) { - p := b.prog[mat] - b.ctx.BindProgram(p.prog) +func (b *blitter) blit(mat materialType, col f32color.RGBA, col1, col2 f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D) { + p := b.pipelines[mat] + b.ctx.BindPipeline(p.pipeline) var uniforms *blitUniforms switch mat { case materialColor: - b.colUniforms.frag.color = col - uniforms = &b.colUniforms.vert.blitUniforms + b.colUniforms.color = col + uniforms = &b.colUniforms.blitUniforms case materialTexture: t1, t2, t3, t4, t5, t6 := uvTrans.Elems() - b.texUniforms.vert.blitUniforms.uvTransformR1 = [4]float32{t1, t2, t3, 0} - b.texUniforms.vert.blitUniforms.uvTransformR2 = [4]float32{t4, t5, t6, 0} - uniforms = &b.texUniforms.vert.blitUniforms + b.texUniforms.blitUniforms.uvTransformR1 = [4]float32{t1, t2, t3, 0} + b.texUniforms.blitUniforms.uvTransformR2 = [4]float32{t4, t5, t6, 0} + uniforms = &b.texUniforms.blitUniforms + case materialLinearGradient: + b.linearGradientUniforms.color1 = col1 + b.linearGradientUniforms.color2 = col2 + + t1, t2, t3, t4, t5, t6 := uvTrans.Elems() + b.linearGradientUniforms.blitUniforms.uvTransformR1 = [4]float32{t1, t2, t3, 0} + b.linearGradientUniforms.blitUniforms.uvTransformR2 = [4]float32{t4, t5, t6, 0} + uniforms = &b.linearGradientUniforms.blitUniforms } - uniforms.z = z uniforms.transform = [4]float32{scale.X, scale.Y, off.X, off.Y} - p.UploadUniforms() - b.ctx.DrawArrays(backend.DrawModeTriangleStrip, 0, 4) + p.UploadUniforms(b.ctx) + b.ctx.DrawArrays(0, 4) } // newUniformBuffer creates a new GPU uniform buffer backed by the // structure uniformBlock points to. -func newUniformBuffer(b backend.Device, uniformBlock interface{}) *uniformBuffer { +func newUniformBuffer(b driver.Device, uniformBlock interface{}) *uniformBuffer { ref := reflect.ValueOf(uniformBlock) // Determine the size of the uniforms structure, *uniforms. size := ref.Elem().Type().Size() // Map the uniforms structure as a byte slice. ptr := (*[1 << 30]byte)(unsafe.Pointer(ref.Pointer()))[:size:size] - ubuf, err := b.NewBuffer(backend.BufferBindingUniforms, len(ptr)) + ubuf, err := b.NewBuffer(driver.BufferBindingUniforms, len(ptr)) if err != nil { panic(err) } @@ -985,36 +1189,19 @@ func (u *uniformBuffer) Release() { u.buf = nil } -func newProgram(prog backend.Program, vertUniforms, fragUniforms *uniformBuffer) *program { - if vertUniforms != nil { - prog.SetVertexUniforms(vertUniforms.buf) - } - if fragUniforms != nil { - prog.SetFragmentUniforms(fragUniforms.buf) - } - return &program{prog: prog, vertUniforms: vertUniforms, fragUniforms: fragUniforms} -} - -func (p *program) UploadUniforms() { - if p.vertUniforms != nil { - p.vertUniforms.Upload() - } - if p.fragUniforms != nil { - p.fragUniforms.Upload() +func (p *pipeline) UploadUniforms(ctx driver.Device) { + if p.uniforms != nil { + p.uniforms.Upload() + ctx.BindUniforms(p.uniforms.buf) } } -func (p *program) Release() { - p.prog.Release() - p.prog = nil - if p.vertUniforms != nil { - p.vertUniforms.Release() - p.vertUniforms = nil - } - if p.fragUniforms != nil { - p.fragUniforms.Release() - p.fragUniforms = nil +func (p *pipeline) Release() { + p.pipeline.Release() + if p.uniforms != nil { + p.uniforms.Release() } + *p = pipeline{} } // texSpaceTransform return the scale and offset that transforms the given subimage @@ -1026,26 +1213,42 @@ func texSpaceTransform(r f32.Rectangle, bounds image.Point) (f32.Point, f32.Poin return scale, offset } +// gradientSpaceTransform transforms stop1 and stop2 to [(0,0), (1,1)]. +func gradientSpaceTransform(clip image.Rectangle, off f32.Point, stop1, stop2 f32.Point) f32.Affine2D { + d := stop2.Sub(stop1) + l := float32(math.Sqrt(float64(d.X*d.X + d.Y*d.Y))) + a := float32(math.Atan2(float64(-d.Y), float64(d.X))) + + // TODO: optimize + zp := f32.Point{} + return f32.Affine2D{}. + Scale(zp, layout.FPt(clip.Size())). // scale to pixel space + Offset(zp.Sub(off).Add(layout.FPt(clip.Min))). // offset to clip space + Offset(zp.Sub(stop1)). // offset to first stop point + Rotate(zp, a). // rotate to align gradient + Scale(zp, f32.Pt(1/l, 1/l)) // scale gradient to right size +} + // clipSpaceTransform returns the scale and offset that transforms the given -// rectangle from a viewport into OpenGL clip space. +// rectangle from a viewport into GPU driver device coordinates. func clipSpaceTransform(r image.Rectangle, viewport image.Point) (f32.Point, f32.Point) { - // First, transform UI coordinates to OpenGL coordinates: + // First, transform UI coordinates to device coordinates: // - // [(-1, +1) (+1, +1)] // [(-1, -1) (+1, -1)] + // [(-1, +1) (+1, +1)] // x, y := float32(r.Min.X), float32(r.Min.Y) w, h := float32(r.Dx()), float32(r.Dy()) vx, vy := 2/float32(viewport.X), 2/float32(viewport.Y) x = x*vx - 1 - y = 1 - y*vy + y = y*vy - 1 w *= vx h *= vy // Then, compute the transformation from the fullscreen quad to // the rectangle at (x, y) and dimensions (w, h). scale := f32.Point{X: w * .5, Y: h * .5} - offset := f32.Point{X: x + w*.5, Y: y - h*.5} + offset := f32.Point{X: x + w*.5, Y: y + h*.5} return scale, offset } @@ -1097,29 +1300,73 @@ func (d *drawOps) writeVertCache(n int) []byte { } // transform, split paths as needed, calculate maxY, bounds and create GPU vertices. -func (d *drawOps) buildVerts(aux []byte, tr f32.Affine2D) (verts []byte, bounds f32.Rectangle) { +func (d *drawOps) buildVerts(pathData []byte, tr f32.Affine2D, outline bool, strWidth float32) (verts []byte, bounds f32.Rectangle) { inf := float32(math.Inf(+1)) d.qs.bounds = f32.Rectangle{ Min: f32.Point{X: inf, Y: inf}, Max: f32.Point{X: -inf, Y: -inf}, } d.qs.d = d - bo := binary.LittleEndian startLength := len(d.vertCache) - for qi := 0; len(aux) >= (ops.QuadSize + 4); qi++ { - d.qs.contour = bo.Uint32(aux) - quad := ops.DecodeQuad(aux[4:]) - quad = quad.Transform(tr) - d.qs.splitAndEncode(quad) + switch { + case strWidth > 0: + // Stroke path. + ss := stroke.StrokeStyle{ + Width: strWidth, + } + quads := stroke.StrokePathCommands(ss, pathData) + for _, quad := range quads { + d.qs.contour = quad.Contour + quad.Quad = quad.Quad.Transform(tr) + + d.qs.splitAndEncode(quad.Quad) + } - aux = aux[ops.QuadSize+4:] + case outline: + decodeToOutlineQuads(&d.qs, tr, pathData) } fillMaxY(d.vertCache[startLength:]) return d.vertCache[startLength:], d.qs.bounds } +// decodeOutlineQuads decodes scene commands, splits them into quadratic béziers +// as needed and feeds them to the supplied splitter. +func decodeToOutlineQuads(qs *quadSplitter, tr f32.Affine2D, pathData []byte) { + for len(pathData) >= scene.CommandSize+4 { + qs.contour = bo.Uint32(pathData) + cmd := ops.DecodeCommand(pathData[4:]) + switch cmd.Op() { + case scene.OpLine: + var q stroke.QuadSegment + q.From, q.To = scene.DecodeLine(cmd) + q.Ctrl = q.From.Add(q.To).Mul(.5) + q = q.Transform(tr) + qs.splitAndEncode(q) + case scene.OpGap: + var q stroke.QuadSegment + q.From, q.To = scene.DecodeGap(cmd) + q.Ctrl = q.From.Add(q.To).Mul(.5) + q = q.Transform(tr) + qs.splitAndEncode(q) + case scene.OpQuad: + var q stroke.QuadSegment + q.From, q.Ctrl, q.To = scene.DecodeQuad(cmd) + q = q.Transform(tr) + qs.splitAndEncode(q) + case scene.OpCubic: + for _, q := range stroke.SplitCubic(scene.DecodeCubic(cmd)) { + q = q.Transform(tr) + qs.splitAndEncode(q) + } + default: + panic("unsupported scene command") + } + pathData = pathData[scene.CommandSize+4:] + } +} + // create GPU vertices for transformed r, find the bounds and establish texture transform. func (d *drawOps) boundsForTransformedRect(r f32.Rectangle, tr f32.Affine2D) (aux []byte, bnd f32.Rectangle, ptr f32.Affine2D) { if isPureOffset(tr) { diff --git a/vendor/gioui.org/gpu/internal/d3d11/d3d11.go b/vendor/gioui.org/gpu/internal/d3d11/d3d11.go new file mode 100644 index 0000000..3ddf7c3 --- /dev/null +++ b/vendor/gioui.org/gpu/internal/d3d11/d3d11.go @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +// This file exists so this package builds on non-Windows platforms. + +package d3d11 diff --git a/vendor/gioui.org/gpu/internal/d3d11/d3d11_windows.go b/vendor/gioui.org/gpu/internal/d3d11/d3d11_windows.go new file mode 100644 index 0000000..08698c3 --- /dev/null +++ b/vendor/gioui.org/gpu/internal/d3d11/d3d11_windows.go @@ -0,0 +1,859 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package d3d11 + +import ( + "errors" + "fmt" + "image" + "math" + "reflect" + "unsafe" + + "golang.org/x/sys/windows" + + "gioui.org/gpu/internal/driver" + "gioui.org/internal/d3d11" + "gioui.org/shader" +) + +type Backend struct { + dev *d3d11.Device + ctx *d3d11.DeviceContext + + // Temporary storage to avoid garbage. + clearColor [4]float32 + viewport d3d11.VIEWPORT + + pipeline *Pipeline + vert struct { + buffer *Buffer + offset int + } + + program *Program + + caps driver.Caps + + floatFormat uint32 +} + +type Pipeline struct { + vert *d3d11.VertexShader + frag *d3d11.PixelShader + layout *d3d11.InputLayout + blend *d3d11.BlendState + stride int + topology driver.Topology +} + +type Texture struct { + backend *Backend + format uint32 + bindings driver.BufferBinding + tex *d3d11.Texture2D + sampler *d3d11.SamplerState + resView *d3d11.ShaderResourceView + uaView *d3d11.UnorderedAccessView + renderTarget *d3d11.RenderTargetView + + width int + height int + foreign bool +} + +type VertexShader struct { + backend *Backend + shader *d3d11.VertexShader + src shader.Sources +} + +type FragmentShader struct { + backend *Backend + shader *d3d11.PixelShader +} + +type Program struct { + backend *Backend + shader *d3d11.ComputeShader +} + +type Buffer struct { + backend *Backend + bind uint32 + buf *d3d11.Buffer + resView *d3d11.ShaderResourceView + uaView *d3d11.UnorderedAccessView + size int + immutable bool +} + +func init() { + driver.NewDirect3D11Device = newDirect3D11Device +} + +func detectFloatFormat(dev *d3d11.Device) (uint32, bool) { + formats := []uint32{ + d3d11.DXGI_FORMAT_R16_FLOAT, + d3d11.DXGI_FORMAT_R32_FLOAT, + d3d11.DXGI_FORMAT_R16G16_FLOAT, + d3d11.DXGI_FORMAT_R32G32_FLOAT, + // These last two are really wasteful, but c'est la vie. + d3d11.DXGI_FORMAT_R16G16B16A16_FLOAT, + d3d11.DXGI_FORMAT_R32G32B32A32_FLOAT, + } + for _, format := range formats { + need := uint32(d3d11.FORMAT_SUPPORT_TEXTURE2D | d3d11.FORMAT_SUPPORT_RENDER_TARGET) + if support, _ := dev.CheckFormatSupport(format); support&need == need { + return format, true + } + } + return 0, false +} + +func newDirect3D11Device(api driver.Direct3D11) (driver.Device, error) { + dev := (*d3d11.Device)(api.Device) + b := &Backend{ + dev: dev, + ctx: dev.GetImmediateContext(), + caps: driver.Caps{ + MaxTextureSize: 2048, // 9.1 maximum + Features: driver.FeatureSRGB, + }, + } + featLvl := dev.GetFeatureLevel() + switch { + case featLvl < d3d11.FEATURE_LEVEL_9_1: + d3d11.IUnknownRelease(unsafe.Pointer(dev), dev.Vtbl.Release) + d3d11.IUnknownRelease(unsafe.Pointer(b.ctx), b.ctx.Vtbl.Release) + return nil, fmt.Errorf("d3d11: feature level too low: %d", featLvl) + case featLvl >= d3d11.FEATURE_LEVEL_11_0: + b.caps.MaxTextureSize = 16384 + b.caps.Features |= driver.FeatureCompute + case featLvl >= d3d11.FEATURE_LEVEL_9_3: + b.caps.MaxTextureSize = 4096 + } + if fmt, ok := detectFloatFormat(dev); ok { + b.floatFormat = fmt + b.caps.Features |= driver.FeatureFloatRenderTargets + } + // Disable backface culling to match OpenGL. + state, err := dev.CreateRasterizerState(&d3d11.RASTERIZER_DESC{ + CullMode: d3d11.CULL_NONE, + FillMode: d3d11.FILL_SOLID, + }) + if err != nil { + return nil, err + } + defer d3d11.IUnknownRelease(unsafe.Pointer(state), state.Vtbl.Release) + b.ctx.RSSetState(state) + return b, nil +} + +func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Texture { + var ( + renderTarget *d3d11.RenderTargetView + ) + if target != nil { + switch t := target.(type) { + case driver.Direct3D11RenderTarget: + renderTarget = (*d3d11.RenderTargetView)(t.RenderTarget) + case *Texture: + renderTarget = t.renderTarget + default: + panic(fmt.Errorf("d3d11: invalid render target type: %T", target)) + } + } + b.ctx.OMSetRenderTargets(renderTarget, nil) + return &Texture{backend: b, renderTarget: renderTarget, foreign: true} +} + +func (b *Backend) CopyTexture(dstTex driver.Texture, dstOrigin image.Point, srcTex driver.Texture, srcRect image.Rectangle) { + dst := (*d3d11.Resource)(unsafe.Pointer(dstTex.(*Texture).tex)) + src := (*d3d11.Resource)(srcTex.(*Texture).tex) + b.ctx.CopySubresourceRegion( + dst, + 0, // Destination subresource. + uint32(dstOrigin.X), uint32(dstOrigin.Y), 0, // Destination coordinates (x, y, z). + src, + 0, // Source subresource. + &d3d11.BOX{ + Left: uint32(srcRect.Min.X), + Top: uint32(srcRect.Min.Y), + Right: uint32(srcRect.Max.X), + Bottom: uint32(srcRect.Max.Y), + Front: 0, + Back: 1, + }, + ) +} + +func (b *Backend) EndFrame() { +} + +func (b *Backend) Caps() driver.Caps { + return b.caps +} + +func (b *Backend) NewTimer() driver.Timer { + panic("timers not supported") +} + +func (b *Backend) IsTimeContinuous() bool { + panic("timers not supported") +} + +func (b *Backend) Release() { + d3d11.IUnknownRelease(unsafe.Pointer(b.ctx), b.ctx.Vtbl.Release) + *b = Backend{} +} + +func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, minFilter, magFilter driver.TextureFilter, bindings driver.BufferBinding) (driver.Texture, error) { + var d3dfmt uint32 + switch format { + case driver.TextureFormatFloat: + d3dfmt = b.floatFormat + case driver.TextureFormatSRGBA: + d3dfmt = d3d11.DXGI_FORMAT_R8G8B8A8_UNORM_SRGB + case driver.TextureFormatRGBA8: + d3dfmt = d3d11.DXGI_FORMAT_R8G8B8A8_UNORM + default: + return nil, fmt.Errorf("unsupported texture format %d", format) + } + tex, err := b.dev.CreateTexture2D(&d3d11.TEXTURE2D_DESC{ + Width: uint32(width), + Height: uint32(height), + MipLevels: 1, + ArraySize: 1, + Format: d3dfmt, + SampleDesc: d3d11.DXGI_SAMPLE_DESC{ + Count: 1, + Quality: 0, + }, + BindFlags: convBufferBinding(bindings), + }) + if err != nil { + return nil, err + } + var ( + sampler *d3d11.SamplerState + resView *d3d11.ShaderResourceView + uaView *d3d11.UnorderedAccessView + fbo *d3d11.RenderTargetView + ) + if bindings&driver.BufferBindingTexture != 0 { + var filter uint32 + switch { + case minFilter == driver.FilterNearest && magFilter == driver.FilterNearest: + filter = d3d11.FILTER_MIN_MAG_MIP_POINT + case minFilter == driver.FilterLinear && magFilter == driver.FilterLinear: + filter = d3d11.FILTER_MIN_MAG_LINEAR_MIP_POINT + default: + d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release) + return nil, fmt.Errorf("unsupported texture filter combination %d, %d", minFilter, magFilter) + } + var err error + sampler, err = b.dev.CreateSamplerState(&d3d11.SAMPLER_DESC{ + Filter: filter, + AddressU: d3d11.TEXTURE_ADDRESS_CLAMP, + AddressV: d3d11.TEXTURE_ADDRESS_CLAMP, + AddressW: d3d11.TEXTURE_ADDRESS_CLAMP, + MaxAnisotropy: 1, + MinLOD: -math.MaxFloat32, + MaxLOD: math.MaxFloat32, + }) + if err != nil { + d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release) + return nil, err + } + resView, err = b.dev.CreateShaderResourceView( + (*d3d11.Resource)(unsafe.Pointer(tex)), + unsafe.Pointer(&d3d11.SHADER_RESOURCE_VIEW_DESC_TEX2D{ + SHADER_RESOURCE_VIEW_DESC: d3d11.SHADER_RESOURCE_VIEW_DESC{ + Format: d3dfmt, + ViewDimension: d3d11.SRV_DIMENSION_TEXTURE2D, + }, + Texture2D: d3d11.TEX2D_SRV{ + MostDetailedMip: 0, + MipLevels: ^uint32(0), + }, + }), + ) + if err != nil { + d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release) + d3d11.IUnknownRelease(unsafe.Pointer(sampler), sampler.Vtbl.Release) + return nil, err + } + } + if bindings&driver.BufferBindingShaderStorageWrite != 0 { + uaView, err = b.dev.CreateUnorderedAccessView( + (*d3d11.Resource)(unsafe.Pointer(tex)), + unsafe.Pointer(&d3d11.UNORDERED_ACCESS_VIEW_DESC_TEX2D{ + UNORDERED_ACCESS_VIEW_DESC: d3d11.UNORDERED_ACCESS_VIEW_DESC{ + Format: d3dfmt, + ViewDimension: d3d11.UAV_DIMENSION_TEXTURE2D, + }, + Texture2D: d3d11.TEX2D_UAV{ + MipSlice: 0, + }, + }), + ) + if err != nil { + if sampler != nil { + d3d11.IUnknownRelease(unsafe.Pointer(sampler), sampler.Vtbl.Release) + } + if resView != nil { + d3d11.IUnknownRelease(unsafe.Pointer(resView), resView.Vtbl.Release) + } + d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release) + return nil, err + } + } + if bindings&driver.BufferBindingFramebuffer != 0 { + resource := (*d3d11.Resource)(unsafe.Pointer(tex)) + fbo, err = b.dev.CreateRenderTargetView(resource) + if err != nil { + if uaView != nil { + d3d11.IUnknownRelease(unsafe.Pointer(uaView), uaView.Vtbl.Release) + } + if sampler != nil { + d3d11.IUnknownRelease(unsafe.Pointer(sampler), sampler.Vtbl.Release) + } + if resView != nil { + d3d11.IUnknownRelease(unsafe.Pointer(resView), resView.Vtbl.Release) + } + d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release) + return nil, err + } + } + return &Texture{backend: b, format: d3dfmt, tex: tex, sampler: sampler, resView: resView, uaView: uaView, renderTarget: fbo, bindings: bindings, width: width, height: height}, nil +} + +func (b *Backend) newInputLayout(vertexShader shader.Sources, layout []driver.InputDesc) (*d3d11.InputLayout, error) { + if len(vertexShader.Inputs) != len(layout) { + return nil, fmt.Errorf("NewInputLayout: got %d inputs, expected %d", len(layout), len(vertexShader.Inputs)) + } + descs := make([]d3d11.INPUT_ELEMENT_DESC, len(layout)) + for i, l := range layout { + inp := vertexShader.Inputs[i] + cname, err := windows.BytePtrFromString(inp.Semantic) + if err != nil { + return nil, err + } + var format uint32 + switch l.Type { + case shader.DataTypeFloat: + switch l.Size { + case 1: + format = d3d11.DXGI_FORMAT_R32_FLOAT + case 2: + format = d3d11.DXGI_FORMAT_R32G32_FLOAT + case 3: + format = d3d11.DXGI_FORMAT_R32G32B32_FLOAT + case 4: + format = d3d11.DXGI_FORMAT_R32G32B32A32_FLOAT + default: + panic("unsupported data size") + } + case shader.DataTypeShort: + switch l.Size { + case 1: + format = d3d11.DXGI_FORMAT_R16_SINT + case 2: + format = d3d11.DXGI_FORMAT_R16G16_SINT + default: + panic("unsupported data size") + } + default: + panic("unsupported data type") + } + descs[i] = d3d11.INPUT_ELEMENT_DESC{ + SemanticName: cname, + SemanticIndex: uint32(inp.SemanticIndex), + Format: format, + AlignedByteOffset: uint32(l.Offset), + } + } + return b.dev.CreateInputLayout(descs, []byte(vertexShader.DXBC)) +} + +func (b *Backend) NewBuffer(typ driver.BufferBinding, size int) (driver.Buffer, error) { + return b.newBuffer(typ, size, nil, false) +} + +func (b *Backend) NewImmutableBuffer(typ driver.BufferBinding, data []byte) (driver.Buffer, error) { + return b.newBuffer(typ, len(data), data, true) +} + +func (b *Backend) newBuffer(typ driver.BufferBinding, size int, data []byte, immutable bool) (*Buffer, error) { + if typ&driver.BufferBindingUniforms != 0 { + if typ != driver.BufferBindingUniforms { + return nil, errors.New("uniform buffers cannot have other bindings") + } + if size%16 != 0 { + return nil, fmt.Errorf("constant buffer size is %d, expected a multiple of 16", size) + } + } + bind := convBufferBinding(typ) + var usage, miscFlags, cpuFlags uint32 + if immutable { + usage = d3d11.USAGE_IMMUTABLE + } + if typ&driver.BufferBindingShaderStorageWrite != 0 { + cpuFlags = d3d11.CPU_ACCESS_READ + } + if typ&(driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite) != 0 { + miscFlags |= d3d11.RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS + } + buf, err := b.dev.CreateBuffer(&d3d11.BUFFER_DESC{ + ByteWidth: uint32(size), + Usage: usage, + BindFlags: bind, + CPUAccessFlags: cpuFlags, + MiscFlags: miscFlags, + }, data) + if err != nil { + return nil, err + } + var ( + resView *d3d11.ShaderResourceView + uaView *d3d11.UnorderedAccessView + ) + if typ&driver.BufferBindingShaderStorageWrite != 0 { + uaView, err = b.dev.CreateUnorderedAccessView( + (*d3d11.Resource)(unsafe.Pointer(buf)), + unsafe.Pointer(&d3d11.UNORDERED_ACCESS_VIEW_DESC_BUFFER{ + UNORDERED_ACCESS_VIEW_DESC: d3d11.UNORDERED_ACCESS_VIEW_DESC{ + Format: d3d11.DXGI_FORMAT_R32_TYPELESS, + ViewDimension: d3d11.UAV_DIMENSION_BUFFER, + }, + Buffer: d3d11.BUFFER_UAV{ + FirstElement: 0, + NumElements: uint32(size / 4), + Flags: d3d11.BUFFER_UAV_FLAG_RAW, + }, + }), + ) + if err != nil { + d3d11.IUnknownRelease(unsafe.Pointer(buf), buf.Vtbl.Release) + return nil, err + } + } else if typ&driver.BufferBindingShaderStorageRead != 0 { + resView, err = b.dev.CreateShaderResourceView( + (*d3d11.Resource)(unsafe.Pointer(buf)), + unsafe.Pointer(&d3d11.SHADER_RESOURCE_VIEW_DESC_BUFFEREX{ + SHADER_RESOURCE_VIEW_DESC: d3d11.SHADER_RESOURCE_VIEW_DESC{ + Format: d3d11.DXGI_FORMAT_R32_TYPELESS, + ViewDimension: d3d11.SRV_DIMENSION_BUFFEREX, + }, + Buffer: d3d11.BUFFEREX_SRV{ + FirstElement: 0, + NumElements: uint32(size / 4), + Flags: d3d11.BUFFEREX_SRV_FLAG_RAW, + }, + }), + ) + if err != nil { + d3d11.IUnknownRelease(unsafe.Pointer(buf), buf.Vtbl.Release) + return nil, err + } + } + return &Buffer{backend: b, buf: buf, bind: bind, size: size, resView: resView, uaView: uaView, immutable: immutable}, nil +} + +func (b *Backend) NewComputeProgram(shader shader.Sources) (driver.Program, error) { + cs, err := b.dev.CreateComputeShader([]byte(shader.DXBC)) + if err != nil { + return nil, err + } + return &Program{backend: b, shader: cs}, nil +} + +func (b *Backend) NewPipeline(desc driver.PipelineDesc) (driver.Pipeline, error) { + vsh := desc.VertexShader.(*VertexShader) + fsh := desc.FragmentShader.(*FragmentShader) + blend, err := b.newBlendState(desc.BlendDesc) + if err != nil { + return nil, err + } + var layout *d3d11.InputLayout + if l := desc.VertexLayout; l.Stride > 0 { + var err error + layout, err = b.newInputLayout(vsh.src, l.Inputs) + if err != nil { + d3d11.IUnknownRelease(unsafe.Pointer(blend), blend.Vtbl.AddRef) + return nil, err + } + } + + // Retain shaders. + vshRef := vsh.shader + fshRef := fsh.shader + d3d11.IUnknownAddRef(unsafe.Pointer(vshRef), vshRef.Vtbl.AddRef) + d3d11.IUnknownAddRef(unsafe.Pointer(fshRef), fshRef.Vtbl.AddRef) + + return &Pipeline{ + vert: vshRef, + frag: fshRef, + layout: layout, + stride: desc.VertexLayout.Stride, + blend: blend, + topology: desc.Topology, + }, nil +} + +func (b *Backend) newBlendState(desc driver.BlendDesc) (*d3d11.BlendState, error) { + var d3ddesc d3d11.BLEND_DESC + t0 := &d3ddesc.RenderTarget[0] + t0.RenderTargetWriteMask = d3d11.COLOR_WRITE_ENABLE_ALL + t0.BlendOp = d3d11.BLEND_OP_ADD + t0.BlendOpAlpha = d3d11.BLEND_OP_ADD + if desc.Enable { + t0.BlendEnable = 1 + } + scol, salpha := toBlendFactor(desc.SrcFactor) + dcol, dalpha := toBlendFactor(desc.DstFactor) + t0.SrcBlend = scol + t0.SrcBlendAlpha = salpha + t0.DestBlend = dcol + t0.DestBlendAlpha = dalpha + return b.dev.CreateBlendState(&d3ddesc) +} + +func (b *Backend) NewVertexShader(src shader.Sources) (driver.VertexShader, error) { + vs, err := b.dev.CreateVertexShader([]byte(src.DXBC)) + if err != nil { + return nil, err + } + return &VertexShader{b, vs, src}, nil +} + +func (b *Backend) NewFragmentShader(src shader.Sources) (driver.FragmentShader, error) { + fs, err := b.dev.CreatePixelShader([]byte(src.DXBC)) + if err != nil { + return nil, err + } + return &FragmentShader{b, fs}, nil +} + +func (b *Backend) Viewport(x, y, width, height int) { + b.viewport = d3d11.VIEWPORT{ + TopLeftX: float32(x), + TopLeftY: float32(y), + Width: float32(width), + Height: float32(height), + MinDepth: 0.0, + MaxDepth: 1.0, + } + b.ctx.RSSetViewports(&b.viewport) +} + +func (b *Backend) DrawArrays(off, count int) { + b.prepareDraw() + b.ctx.Draw(uint32(count), uint32(off)) +} + +func (b *Backend) DrawElements(off, count int) { + b.prepareDraw() + b.ctx.DrawIndexed(uint32(count), uint32(off), 0) +} + +func (b *Backend) prepareDraw() { + p := b.pipeline + if p == nil { + return + } + b.ctx.VSSetShader(p.vert) + b.ctx.PSSetShader(p.frag) + b.ctx.IASetInputLayout(p.layout) + b.ctx.OMSetBlendState(p.blend, nil, 0xffffffff) + if b.vert.buffer != nil { + b.ctx.IASetVertexBuffers(b.vert.buffer.buf, uint32(p.stride), uint32(b.vert.offset)) + } + var topology uint32 + switch p.topology { + case driver.TopologyTriangles: + topology = d3d11.PRIMITIVE_TOPOLOGY_TRIANGLELIST + case driver.TopologyTriangleStrip: + topology = d3d11.PRIMITIVE_TOPOLOGY_TRIANGLESTRIP + default: + panic("unsupported draw mode") + } + b.ctx.IASetPrimitiveTopology(topology) +} + +func (b *Backend) BindImageTexture(unit int, tex driver.Texture) { + t := tex.(*Texture) + if t.uaView != nil { + b.ctx.CSSetUnorderedAccessViews(uint32(unit), t.uaView) + } else { + b.ctx.CSSetShaderResources(uint32(unit), t.resView) + } +} + +func (b *Backend) DispatchCompute(x, y, z int) { + b.ctx.CSSetShader(b.program.shader) + b.ctx.Dispatch(uint32(x), uint32(y), uint32(z)) +} + +func (t *Texture) Upload(offset, size image.Point, pixels []byte, stride int) { + if stride == 0 { + stride = size.X * 4 + } + dst := &d3d11.BOX{ + Left: uint32(offset.X), + Top: uint32(offset.Y), + Right: uint32(offset.X + size.X), + Bottom: uint32(offset.Y + size.Y), + Front: 0, + Back: 1, + } + res := (*d3d11.Resource)(unsafe.Pointer(t.tex)) + t.backend.ctx.UpdateSubresource(res, dst, uint32(stride), uint32(len(pixels)), pixels) +} + +func (t *Texture) Release() { + if t.foreign { + panic("texture not created by NewTexture") + } + if t.renderTarget != nil { + d3d11.IUnknownRelease(unsafe.Pointer(t.renderTarget), t.renderTarget.Vtbl.Release) + } + if t.sampler != nil { + d3d11.IUnknownRelease(unsafe.Pointer(t.sampler), t.sampler.Vtbl.Release) + } + if t.resView != nil { + d3d11.IUnknownRelease(unsafe.Pointer(t.resView), t.resView.Vtbl.Release) + } + if t.uaView != nil { + d3d11.IUnknownRelease(unsafe.Pointer(t.uaView), t.uaView.Vtbl.Release) + } + d3d11.IUnknownRelease(unsafe.Pointer(t.tex), t.tex.Vtbl.Release) + *t = Texture{} +} + +func (b *Backend) PrepareTexture(tex driver.Texture) {} + +func (b *Backend) BindTexture(unit int, tex driver.Texture) { + t := tex.(*Texture) + b.ctx.PSSetSamplers(uint32(unit), t.sampler) + b.ctx.PSSetShaderResources(uint32(unit), t.resView) +} + +func (b *Backend) BindPipeline(pipe driver.Pipeline) { + b.pipeline = pipe.(*Pipeline) +} + +func (b *Backend) BindProgram(prog driver.Program) { + b.program = prog.(*Program) +} + +func (s *VertexShader) Release() { + d3d11.IUnknownRelease(unsafe.Pointer(s.shader), s.shader.Vtbl.Release) + *s = VertexShader{} +} + +func (s *FragmentShader) Release() { + d3d11.IUnknownRelease(unsafe.Pointer(s.shader), s.shader.Vtbl.Release) + *s = FragmentShader{} +} + +func (s *Program) Release() { + d3d11.IUnknownRelease(unsafe.Pointer(s.shader), s.shader.Vtbl.Release) + *s = Program{} +} + +func (p *Pipeline) Release() { + d3d11.IUnknownRelease(unsafe.Pointer(p.vert), p.vert.Vtbl.Release) + d3d11.IUnknownRelease(unsafe.Pointer(p.frag), p.frag.Vtbl.Release) + d3d11.IUnknownRelease(unsafe.Pointer(p.blend), p.blend.Vtbl.Release) + if l := p.layout; l != nil { + d3d11.IUnknownRelease(unsafe.Pointer(l), l.Vtbl.Release) + } + *p = Pipeline{} +} + +func (b *Backend) BindStorageBuffer(binding int, buffer driver.Buffer) { + buf := buffer.(*Buffer) + if buf.resView != nil { + b.ctx.CSSetShaderResources(uint32(binding), buf.resView) + } else { + b.ctx.CSSetUnorderedAccessViews(uint32(binding), buf.uaView) + } +} + +func (b *Backend) BindUniforms(buffer driver.Buffer) { + buf := buffer.(*Buffer) + b.ctx.VSSetConstantBuffers(buf.buf) + b.ctx.PSSetConstantBuffers(buf.buf) +} + +func (b *Backend) BindVertexBuffer(buf driver.Buffer, offset int) { + b.vert.buffer = buf.(*Buffer) + b.vert.offset = offset +} + +func (b *Backend) BindIndexBuffer(buf driver.Buffer) { + b.ctx.IASetIndexBuffer(buf.(*Buffer).buf, d3d11.DXGI_FORMAT_R16_UINT, 0) +} + +func (b *Buffer) Download(dst []byte) error { + res := (*d3d11.Resource)(unsafe.Pointer(b.buf)) + resMap, err := b.backend.ctx.Map(res, 0, d3d11.MAP_READ, 0) + if err != nil { + return fmt.Errorf("d3d11: %v", err) + } + defer b.backend.ctx.Unmap(res, 0) + data := sliceOf(resMap.PData, len(dst)) + copy(dst, data) + return nil +} + +func (b *Buffer) Upload(data []byte) { + var dst *d3d11.BOX + if len(data) < b.size { + dst = &d3d11.BOX{ + Left: 0, + Right: uint32(len(data)), + Top: 0, + Bottom: 1, + Front: 0, + Back: 1, + } + } + b.backend.ctx.UpdateSubresource((*d3d11.Resource)(unsafe.Pointer(b.buf)), dst, 0, 0, data) +} + +func (b *Buffer) Release() { + if b.resView != nil { + d3d11.IUnknownRelease(unsafe.Pointer(b.resView), b.resView.Vtbl.Release) + } + if b.uaView != nil { + d3d11.IUnknownRelease(unsafe.Pointer(b.uaView), b.uaView.Vtbl.Release) + } + d3d11.IUnknownRelease(unsafe.Pointer(b.buf), b.buf.Vtbl.Release) + *b = Buffer{} +} + +func (t *Texture) ReadPixels(src image.Rectangle, pixels []byte, stride int) error { + w, h := src.Dx(), src.Dy() + tex, err := t.backend.dev.CreateTexture2D(&d3d11.TEXTURE2D_DESC{ + Width: uint32(w), + Height: uint32(h), + MipLevels: 1, + ArraySize: 1, + Format: t.format, + SampleDesc: d3d11.DXGI_SAMPLE_DESC{ + Count: 1, + Quality: 0, + }, + Usage: d3d11.USAGE_STAGING, + CPUAccessFlags: d3d11.CPU_ACCESS_READ, + }) + if err != nil { + return fmt.Errorf("ReadPixels: %v", err) + } + defer d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release) + res := (*d3d11.Resource)(unsafe.Pointer(tex)) + t.backend.ctx.CopySubresourceRegion( + res, + 0, // Destination subresource. + 0, 0, 0, // Destination coordinates (x, y, z). + (*d3d11.Resource)(t.tex), + 0, // Source subresource. + &d3d11.BOX{ + Left: uint32(src.Min.X), + Top: uint32(src.Min.Y), + Right: uint32(src.Max.X), + Bottom: uint32(src.Max.Y), + Front: 0, + Back: 1, + }, + ) + resMap, err := t.backend.ctx.Map(res, 0, d3d11.MAP_READ, 0) + if err != nil { + return fmt.Errorf("ReadPixels: %v", err) + } + defer t.backend.ctx.Unmap(res, 0) + srcPitch := stride + dstPitch := int(resMap.RowPitch) + mapSize := dstPitch * h + data := sliceOf(resMap.PData, mapSize) + width := w * 4 + for r := 0; r < h; r++ { + pixels := pixels[r*srcPitch:] + copy(pixels[:width], data[r*dstPitch:]) + } + return nil +} + +func (b *Backend) BeginCompute() { +} + +func (b *Backend) EndCompute() { +} + +func (b *Backend) BeginRenderPass(tex driver.Texture, d driver.LoadDesc) { + t := tex.(*Texture) + b.ctx.OMSetRenderTargets(t.renderTarget, nil) + if d.Action == driver.LoadActionClear { + c := d.ClearColor + b.clearColor = [4]float32{c.R, c.G, c.B, c.A} + b.ctx.ClearRenderTargetView(t.renderTarget, &b.clearColor) + } +} + +func (b *Backend) EndRenderPass() { +} + +func (f *Texture) ImplementsRenderTarget() {} + +func convBufferBinding(typ driver.BufferBinding) uint32 { + var bindings uint32 + if typ&driver.BufferBindingVertices != 0 { + bindings |= d3d11.BIND_VERTEX_BUFFER + } + if typ&driver.BufferBindingIndices != 0 { + bindings |= d3d11.BIND_INDEX_BUFFER + } + if typ&driver.BufferBindingUniforms != 0 { + bindings |= d3d11.BIND_CONSTANT_BUFFER + } + if typ&driver.BufferBindingTexture != 0 { + bindings |= d3d11.BIND_SHADER_RESOURCE + } + if typ&driver.BufferBindingFramebuffer != 0 { + bindings |= d3d11.BIND_RENDER_TARGET + } + if typ&driver.BufferBindingShaderStorageWrite != 0 { + bindings |= d3d11.BIND_UNORDERED_ACCESS + } else if typ&driver.BufferBindingShaderStorageRead != 0 { + bindings |= d3d11.BIND_SHADER_RESOURCE + } + return bindings +} + +func toBlendFactor(f driver.BlendFactor) (uint32, uint32) { + switch f { + case driver.BlendFactorOne: + return d3d11.BLEND_ONE, d3d11.BLEND_ONE + case driver.BlendFactorOneMinusSrcAlpha: + return d3d11.BLEND_INV_SRC_ALPHA, d3d11.BLEND_INV_SRC_ALPHA + case driver.BlendFactorZero: + return d3d11.BLEND_ZERO, d3d11.BLEND_ZERO + case driver.BlendFactorDstColor: + return d3d11.BLEND_DEST_COLOR, d3d11.BLEND_DEST_ALPHA + default: + panic("unsupported blend source factor") + } +} + +// sliceOf returns a slice from a (native) pointer. +func sliceOf(ptr uintptr, cap int) []byte { + var data []byte + h := (*reflect.SliceHeader)(unsafe.Pointer(&data)) + h.Data = ptr + h.Cap = cap + h.Len = cap + return data +} diff --git a/vendor/gioui.org/gpu/internal/driver/api.go b/vendor/gioui.org/gpu/internal/driver/api.go new file mode 100644 index 0000000..9a762a6 --- /dev/null +++ b/vendor/gioui.org/gpu/internal/driver/api.go @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package driver + +import ( + "fmt" + "unsafe" + + "gioui.org/internal/gl" +) + +// See gpu/api.go for documentation for the API types. + +type API interface { + implementsAPI() +} + +type RenderTarget interface { + ImplementsRenderTarget() +} + +type OpenGLRenderTarget gl.Framebuffer + +type Direct3D11RenderTarget struct { + // RenderTarget is a *ID3D11RenderTargetView. + RenderTarget unsafe.Pointer +} + +type MetalRenderTarget struct { + // Texture is a MTLTexture. + Texture uintptr +} + +type VulkanRenderTarget struct { + // WaitSem is a VkSemaphore that must signaled before accessing Framebuffer. + WaitSem uint64 + // SignalSem is a VkSemaphore that signal access to Framebuffer is complete. + SignalSem uint64 + // Image is the VkImage to render into. + Image uint64 + // Framebuffer is a VkFramebuffer for Image. + Framebuffer uint64 +} + +type OpenGL struct { + // ES forces the use of ANGLE OpenGL ES libraries on macOS. It is + // ignored on all other platforms. + ES bool + // Context contains the WebGL context for WebAssembly platforms. It is + // empty for all other platforms; an OpenGL context is assumed current when + // calling NewDevice. + Context gl.Context + // Shared instructs users of the context to restore the GL state after + // use. + Shared bool +} + +type Direct3D11 struct { + // Device contains a *ID3D11Device. + Device unsafe.Pointer +} + +type Metal struct { + // Device is an MTLDevice. + Device uintptr + // Queue is a MTLCommandQueue. + Queue uintptr + // PixelFormat is the MTLPixelFormat of the default framebuffer. + PixelFormat int +} + +type Vulkan struct { + // PhysDevice is a VkPhysicalDevice. + PhysDevice unsafe.Pointer + // Device is a VkDevice. + Device unsafe.Pointer + // QueueFamily is the queue familily index of the queue. + QueueFamily int + // QueueIndex is the logical queue index of the queue. + QueueIndex int + // Format is a VkFormat that matches render targets. + Format int +} + +// API specific device constructors. +var ( + NewOpenGLDevice func(api OpenGL) (Device, error) + NewDirect3D11Device func(api Direct3D11) (Device, error) + NewMetalDevice func(api Metal) (Device, error) + NewVulkanDevice func(api Vulkan) (Device, error) +) + +// NewDevice creates a new Device given the api. +// +// Note that the device does not assume ownership of the resources contained in +// api; the caller must ensure the resources are valid until the device is +// released. +func NewDevice(api API) (Device, error) { + switch api := api.(type) { + case OpenGL: + if NewOpenGLDevice != nil { + return NewOpenGLDevice(api) + } + case Direct3D11: + if NewDirect3D11Device != nil { + return NewDirect3D11Device(api) + } + case Metal: + if NewMetalDevice != nil { + return NewMetalDevice(api) + } + case Vulkan: + if NewVulkanDevice != nil { + return NewVulkanDevice(api) + } + } + return nil, fmt.Errorf("driver: no driver available for the API %T", api) +} + +func (OpenGL) implementsAPI() {} +func (Direct3D11) implementsAPI() {} +func (Metal) implementsAPI() {} +func (Vulkan) implementsAPI() {} +func (OpenGLRenderTarget) ImplementsRenderTarget() {} +func (Direct3D11RenderTarget) ImplementsRenderTarget() {} +func (MetalRenderTarget) ImplementsRenderTarget() {} +func (VulkanRenderTarget) ImplementsRenderTarget() {} diff --git a/vendor/gioui.org/gpu/internal/driver/driver.go b/vendor/gioui.org/gpu/internal/driver/driver.go new file mode 100644 index 0000000..58cb89b --- /dev/null +++ b/vendor/gioui.org/gpu/internal/driver/driver.go @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package driver + +import ( + "errors" + "image" + "time" + + "gioui.org/internal/f32color" + "gioui.org/shader" +) + +// Device represents the abstraction of underlying GPU +// APIs such as OpenGL, Direct3D useful for rendering Gio +// operations. +type Device interface { + BeginFrame(target RenderTarget, clear bool, viewport image.Point) Texture + EndFrame() + Caps() Caps + NewTimer() Timer + // IsContinuousTime reports whether all timer measurements + // are valid at the point of call. + IsTimeContinuous() bool + NewTexture(format TextureFormat, width, height int, minFilter, magFilter TextureFilter, bindings BufferBinding) (Texture, error) + NewImmutableBuffer(typ BufferBinding, data []byte) (Buffer, error) + NewBuffer(typ BufferBinding, size int) (Buffer, error) + NewComputeProgram(shader shader.Sources) (Program, error) + NewVertexShader(src shader.Sources) (VertexShader, error) + NewFragmentShader(src shader.Sources) (FragmentShader, error) + NewPipeline(desc PipelineDesc) (Pipeline, error) + + Viewport(x, y, width, height int) + DrawArrays(off, count int) + DrawElements(off, count int) + + BeginRenderPass(t Texture, desc LoadDesc) + EndRenderPass() + PrepareTexture(t Texture) + BindProgram(p Program) + BindPipeline(p Pipeline) + BindTexture(unit int, t Texture) + BindVertexBuffer(b Buffer, offset int) + BindIndexBuffer(b Buffer) + BindImageTexture(unit int, texture Texture) + BindUniforms(buf Buffer) + BindStorageBuffer(binding int, buf Buffer) + + BeginCompute() + EndCompute() + CopyTexture(dst Texture, dstOrigin image.Point, src Texture, srcRect image.Rectangle) + DispatchCompute(x, y, z int) + + Release() +} + +var ErrDeviceLost = errors.New("GPU device lost") + +type LoadDesc struct { + Action LoadAction + ClearColor f32color.RGBA +} + +type Pipeline interface { + Release() +} + +type PipelineDesc struct { + VertexShader VertexShader + FragmentShader FragmentShader + VertexLayout VertexLayout + BlendDesc BlendDesc + PixelFormat TextureFormat + Topology Topology +} + +type VertexLayout struct { + Inputs []InputDesc + Stride int +} + +// InputDesc describes a vertex attribute as laid out in a Buffer. +type InputDesc struct { + Type shader.DataType + Size int + + Offset int +} + +type BlendDesc struct { + Enable bool + SrcFactor, DstFactor BlendFactor +} + +type BlendFactor uint8 + +type Topology uint8 + +type TextureFilter uint8 +type TextureFormat uint8 + +type BufferBinding uint8 + +type LoadAction uint8 + +type Features uint + +type Caps struct { + // BottomLeftOrigin is true if the driver has the origin in the lower left + // corner. The OpenGL driver returns true. + BottomLeftOrigin bool + Features Features + MaxTextureSize int +} + +type VertexShader interface { + Release() +} + +type FragmentShader interface { + Release() +} + +type Program interface { + Release() +} + +type Buffer interface { + Release() + Upload(data []byte) + Download(data []byte) error +} + +type Timer interface { + Begin() + End() + Duration() (time.Duration, bool) + Release() +} + +type Texture interface { + RenderTarget + Upload(offset, size image.Point, pixels []byte, stride int) + ReadPixels(src image.Rectangle, pixels []byte, stride int) error + Release() +} + +const ( + BufferBindingIndices BufferBinding = 1 << iota + BufferBindingVertices + BufferBindingUniforms + BufferBindingTexture + BufferBindingFramebuffer + BufferBindingShaderStorageRead + BufferBindingShaderStorageWrite +) + +const ( + TextureFormatSRGBA TextureFormat = iota + TextureFormatFloat + TextureFormatRGBA8 + // TextureFormatOutput denotes the format used by the output framebuffer. + TextureFormatOutput +) + +const ( + FilterNearest TextureFilter = iota + FilterLinear +) + +const ( + FeatureTimers Features = 1 << iota + FeatureFloatRenderTargets + FeatureCompute + FeatureSRGB +) + +const ( + TopologyTriangleStrip Topology = iota + TopologyTriangles +) + +const ( + BlendFactorOne BlendFactor = iota + BlendFactorOneMinusSrcAlpha + BlendFactorZero + BlendFactorDstColor +) + +const ( + LoadActionKeep LoadAction = iota + LoadActionClear + LoadActionInvalidate +) + +var ErrContentLost = errors.New("buffer content lost") + +func (f Features) Has(feats Features) bool { + return f&feats == feats +} + +func DownloadImage(d Device, t Texture, img *image.RGBA) error { + r := img.Bounds() + if err := t.ReadPixels(r, img.Pix, img.Stride); err != nil { + return err + } + if d.Caps().BottomLeftOrigin { + // OpenGL origin is in the lower-left corner. Flip the image to + // match. + flipImageY(r.Dx()*4, r.Dy(), img.Pix) + } + return nil +} + +func flipImageY(stride, height int, pixels []byte) { + // Flip image in y-direction. OpenGL's origin is in the lower + // left corner. + row := make([]uint8, stride) + for y := 0; y < height/2; y++ { + y1 := height - y - 1 + dest := y1 * stride + src := y * stride + copy(row, pixels[dest:]) + copy(pixels[dest:], pixels[src:src+len(row)]) + copy(pixels[src:], row) + } +} + +func UploadImage(t Texture, offset image.Point, img *image.RGBA) { + var pixels []byte + size := img.Bounds().Size() + min := img.Rect.Min + start := img.PixOffset(min.X, min.Y) + end := img.PixOffset(min.X+size.X, min.Y+size.Y-1) + pixels = img.Pix[start:end] + t.Upload(offset, size, pixels, img.Stride) +} diff --git a/vendor/gioui.org/gpu/internal/metal/metal.go b/vendor/gioui.org/gpu/internal/metal/metal.go new file mode 100644 index 0000000..b9739af --- /dev/null +++ b/vendor/gioui.org/gpu/internal/metal/metal.go @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +// This file exists so this package builds on non-Darwin platforms. + +package metal diff --git a/vendor/gioui.org/gpu/internal/metal/metal_darwin.go b/vendor/gioui.org/gpu/internal/metal/metal_darwin.go new file mode 100644 index 0000000..c180731 --- /dev/null +++ b/vendor/gioui.org/gpu/internal/metal/metal_darwin.go @@ -0,0 +1,1141 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package metal + +import ( + "errors" + "fmt" + "image" + "unsafe" + + "gioui.org/gpu/internal/driver" + "gioui.org/shader" +) + +/* +#cgo CFLAGS: -Werror -xobjective-c -fmodules -fobjc-arc +#cgo LDFLAGS: -framework CoreGraphics + +@import Metal; + +#include <CoreFoundation/CoreFoundation.h> +#include <Metal/Metal.h> + +typedef struct { + void *addr; + NSUInteger size; +} slice; + +static CFTypeRef queueNewBuffer(CFTypeRef queueRef) { + @autoreleasepool { + id<MTLCommandQueue> queue = (__bridge id<MTLCommandQueue>)queueRef; + return CFBridgingRetain([queue commandBuffer]); + } +} + +static void cmdBufferCommit(CFTypeRef cmdBufRef) { + @autoreleasepool { + id<MTLCommandBuffer> cmdBuf = (__bridge id<MTLCommandBuffer>)cmdBufRef; + [cmdBuf commit]; + } +} + +static void cmdBufferWaitUntilCompleted(CFTypeRef cmdBufRef) { + @autoreleasepool { + id<MTLCommandBuffer> cmdBuf = (__bridge id<MTLCommandBuffer>)cmdBufRef; + [cmdBuf waitUntilCompleted]; + } +} + +static CFTypeRef cmdBufferRenderEncoder(CFTypeRef cmdBufRef, CFTypeRef textureRef, MTLLoadAction act, float r, float g, float b, float a) { + @autoreleasepool { + id<MTLCommandBuffer> cmdBuf = (__bridge id<MTLCommandBuffer>)cmdBufRef; + MTLRenderPassDescriptor *desc = [MTLRenderPassDescriptor new]; + desc.colorAttachments[0].texture = (__bridge id<MTLTexture>)textureRef; + desc.colorAttachments[0].loadAction = act; + desc.colorAttachments[0].clearColor = MTLClearColorMake(r, g, b, a); + return CFBridgingRetain([cmdBuf renderCommandEncoderWithDescriptor:desc]); + } +} + +static CFTypeRef cmdBufferComputeEncoder(CFTypeRef cmdBufRef) { + @autoreleasepool { + id<MTLCommandBuffer> cmdBuf = (__bridge id<MTLCommandBuffer>)cmdBufRef; + return CFBridgingRetain([cmdBuf computeCommandEncoder]); + } +} + +static CFTypeRef cmdBufferBlitEncoder(CFTypeRef cmdBufRef) { + @autoreleasepool { + id<MTLCommandBuffer> cmdBuf = (__bridge id<MTLCommandBuffer>)cmdBufRef; + return CFBridgingRetain([cmdBuf blitCommandEncoder]); + } +} + +static void renderEncEnd(CFTypeRef renderEncRef) { + @autoreleasepool { + id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef; + [enc endEncoding]; + } +} + +static void renderEncViewport(CFTypeRef renderEncRef, MTLViewport viewport) { + @autoreleasepool { + id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef; + [enc setViewport:viewport]; + } +} + +static void renderEncSetFragmentTexture(CFTypeRef renderEncRef, NSUInteger index, CFTypeRef texRef) { + @autoreleasepool { + id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef; + id<MTLTexture> tex = (__bridge id<MTLTexture>)texRef; + [enc setFragmentTexture:tex atIndex:index]; + } +} + +static void renderEncSetFragmentSamplerState(CFTypeRef renderEncRef, NSUInteger index, CFTypeRef samplerRef) { + @autoreleasepool { + id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef; + id<MTLSamplerState> sampler = (__bridge id<MTLSamplerState>)samplerRef; + [enc setFragmentSamplerState:sampler atIndex:index]; + } +} + +static void renderEncSetVertexBuffer(CFTypeRef renderEncRef, CFTypeRef bufRef, NSUInteger idx, NSUInteger offset) { + @autoreleasepool { + id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef; + id<MTLBuffer> buf = (__bridge id<MTLBuffer>)bufRef; + [enc setVertexBuffer:buf offset:offset atIndex:idx]; + } +} + +static void renderEncSetFragmentBuffer(CFTypeRef renderEncRef, CFTypeRef bufRef, NSUInteger idx, NSUInteger offset) { + @autoreleasepool { + id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef; + id<MTLBuffer> buf = (__bridge id<MTLBuffer>)bufRef; + [enc setFragmentBuffer:buf offset:offset atIndex:idx]; + } +} + +static void renderEncSetFragmentBytes(CFTypeRef renderEncRef, const void *bytes, NSUInteger length, NSUInteger idx) { + @autoreleasepool { + id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef; + [enc setFragmentBytes:bytes length:length atIndex:idx]; + } +} + +static void renderEncSetVertexBytes(CFTypeRef renderEncRef, const void *bytes, NSUInteger length, NSUInteger idx) { + @autoreleasepool { + id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef; + [enc setVertexBytes:bytes length:length atIndex:idx]; + } +} + +static void renderEncSetRenderPipelineState(CFTypeRef renderEncRef, CFTypeRef pipeRef) { + @autoreleasepool { + id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef; + id<MTLRenderPipelineState> pipe = (__bridge id<MTLRenderPipelineState>)pipeRef; + [enc setRenderPipelineState:pipe]; + } +} + +static void renderEncDrawPrimitives(CFTypeRef renderEncRef, MTLPrimitiveType type, NSUInteger start, NSUInteger count) { + @autoreleasepool { + id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef; + [enc drawPrimitives:type vertexStart:start vertexCount:count]; + } +} + +static void renderEncDrawIndexedPrimitives(CFTypeRef renderEncRef, MTLPrimitiveType type, CFTypeRef bufRef, NSUInteger offset, NSUInteger count) { + @autoreleasepool { + id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef; + id<MTLBuffer> buf = (__bridge id<MTLBuffer>)bufRef; + [enc drawIndexedPrimitives:type indexCount:count indexType:MTLIndexTypeUInt16 indexBuffer:buf indexBufferOffset:offset]; + } +} + +static void computeEncSetPipeline(CFTypeRef computeEncRef, CFTypeRef pipeRef) { + @autoreleasepool { + id<MTLComputeCommandEncoder> enc = (__bridge id<MTLComputeCommandEncoder>)computeEncRef; + id<MTLComputePipelineState> pipe = (__bridge id<MTLComputePipelineState>)pipeRef; + [enc setComputePipelineState:pipe]; + } +} + +static void computeEncSetTexture(CFTypeRef computeEncRef, NSUInteger index, CFTypeRef texRef) { + @autoreleasepool { + id<MTLComputeCommandEncoder> enc = (__bridge id<MTLComputeCommandEncoder>)computeEncRef; + id<MTLTexture> tex = (__bridge id<MTLTexture>)texRef; + [enc setTexture:tex atIndex:index]; + } +} + +static void computeEncEnd(CFTypeRef computeEncRef) { + @autoreleasepool { + id<MTLComputeCommandEncoder> enc = (__bridge id<MTLComputeCommandEncoder>)computeEncRef; + [enc endEncoding]; + } +} + +static void computeEncSetBuffer(CFTypeRef computeEncRef, NSUInteger index, CFTypeRef bufRef) { + @autoreleasepool { + id<MTLComputeCommandEncoder> enc = (__bridge id<MTLComputeCommandEncoder>)computeEncRef; + id<MTLBuffer> buf = (__bridge id<MTLBuffer>)bufRef; + [enc setBuffer:buf offset:0 atIndex:index]; + } +} + +static void computeEncDispatch(CFTypeRef computeEncRef, MTLSize threadgroupsPerGrid, MTLSize threadsPerThreadgroup) { + @autoreleasepool { + id<MTLComputeCommandEncoder> enc = (__bridge id<MTLComputeCommandEncoder>)computeEncRef; + [enc dispatchThreadgroups:threadgroupsPerGrid threadsPerThreadgroup:threadsPerThreadgroup]; + } +} + +static void computeEncSetBytes(CFTypeRef computeEncRef, const void *bytes, NSUInteger length, NSUInteger index) { + @autoreleasepool { + id<MTLComputeCommandEncoder> enc = (__bridge id<MTLComputeCommandEncoder>)computeEncRef; + [enc setBytes:bytes length:length atIndex:index]; + } +} + +static void blitEncEnd(CFTypeRef blitEncRef) { + @autoreleasepool { + id<MTLBlitCommandEncoder> enc = (__bridge id<MTLBlitCommandEncoder>)blitEncRef; + [enc endEncoding]; + } +} + +static void blitEncCopyFromTexture(CFTypeRef blitEncRef, CFTypeRef srcRef, MTLOrigin srcOrig, MTLSize srcSize, CFTypeRef dstRef, MTLOrigin dstOrig) { + @autoreleasepool { + id<MTLBlitCommandEncoder> enc = (__bridge id<MTLBlitCommandEncoder>)blitEncRef; + id<MTLTexture> src = (__bridge id<MTLTexture>)srcRef; + id<MTLTexture> dst = (__bridge id<MTLTexture>)dstRef; + [enc copyFromTexture:src + sourceSlice:0 + sourceLevel:0 + sourceOrigin:srcOrig + sourceSize:srcSize + toTexture:dst + destinationSlice:0 + destinationLevel:0 + destinationOrigin:dstOrig]; + } +} + +static void blitEncCopyBufferToTexture(CFTypeRef blitEncRef, CFTypeRef bufRef, CFTypeRef texRef, NSUInteger offset, NSUInteger stride, NSUInteger length, MTLSize dims, MTLOrigin orig) { + @autoreleasepool { + id<MTLBlitCommandEncoder> enc = (__bridge id<MTLBlitCommandEncoder>)blitEncRef; + id<MTLBuffer> src = (__bridge id<MTLBuffer>)bufRef; + id<MTLTexture> dst = (__bridge id<MTLTexture>)texRef; + [enc copyFromBuffer:src + sourceOffset:offset + sourceBytesPerRow:stride + sourceBytesPerImage:length + sourceSize:dims + toTexture:dst + destinationSlice:0 + destinationLevel:0 + destinationOrigin:orig]; + } +} + +static void blitEncCopyTextureToBuffer(CFTypeRef blitEncRef, CFTypeRef texRef, CFTypeRef bufRef, NSUInteger offset, NSUInteger stride, NSUInteger length, MTLSize dims, MTLOrigin orig) { + @autoreleasepool { + id<MTLBlitCommandEncoder> enc = (__bridge id<MTLBlitCommandEncoder>)blitEncRef; + id<MTLTexture> src = (__bridge id<MTLTexture>)texRef; + id<MTLBuffer> dst = (__bridge id<MTLBuffer>)bufRef; + [enc copyFromTexture:src + sourceSlice:0 + sourceLevel:0 + sourceOrigin:orig + sourceSize:dims + toBuffer:dst + destinationOffset:offset + destinationBytesPerRow:stride + destinationBytesPerImage:length]; + } +} + +static void blitEncCopyBufferToBuffer(CFTypeRef blitEncRef, CFTypeRef srcRef, CFTypeRef dstRef, NSUInteger srcOff, NSUInteger dstOff, NSUInteger size) { + @autoreleasepool { + id<MTLBlitCommandEncoder> enc = (__bridge id<MTLBlitCommandEncoder>)blitEncRef; + id<MTLBuffer> src = (__bridge id<MTLBuffer>)srcRef; + id<MTLBuffer> dst = (__bridge id<MTLBuffer>)dstRef; + [enc copyFromBuffer:src + sourceOffset:srcOff + toBuffer:dst + destinationOffset:dstOff + size:size]; + } +} + +static CFTypeRef newTexture(CFTypeRef devRef, NSUInteger width, NSUInteger height, MTLPixelFormat format, MTLTextureUsage usage) { + @autoreleasepool { + id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef; + MTLTextureDescriptor *mtlDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: format + width: width + height: height + mipmapped: NO]; + mtlDesc.usage = usage; + mtlDesc.storageMode = MTLStorageModePrivate; + return CFBridgingRetain([dev newTextureWithDescriptor:mtlDesc]); + } +} + +static CFTypeRef newSampler(CFTypeRef devRef, MTLSamplerMinMagFilter minFilter, MTLSamplerMinMagFilter magFilter) { + @autoreleasepool { + id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef; + MTLSamplerDescriptor *desc = [MTLSamplerDescriptor new]; + desc.minFilter = minFilter; + desc.magFilter = magFilter; + return CFBridgingRetain([dev newSamplerStateWithDescriptor:desc]); + } +} + +static CFTypeRef newBuffer(CFTypeRef devRef, NSUInteger size, MTLResourceOptions opts) { + @autoreleasepool { + id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef; + id<MTLBuffer> buf = [dev newBufferWithLength:size + options:opts]; + return CFBridgingRetain(buf); + } +} + +static slice bufferContents(CFTypeRef bufRef) { + @autoreleasepool { + id<MTLBuffer> buf = (__bridge id<MTLBuffer>)bufRef; + slice s = {.addr = [buf contents], .size = [buf length]}; + return s; + } +} + +static CFTypeRef newLibrary(CFTypeRef devRef, char *name, void *mtllib, size_t size) { + @autoreleasepool { + id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef; + dispatch_data_t data = dispatch_data_create(mtllib, size, DISPATCH_TARGET_QUEUE_DEFAULT, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + id<MTLLibrary> lib = [dev newLibraryWithData:data error:nil]; + lib.label = [NSString stringWithUTF8String:name]; + return CFBridgingRetain(lib); + } +} + +static CFTypeRef libraryNewFunction(CFTypeRef libRef, char *funcName) { + @autoreleasepool { + id<MTLLibrary> lib = (__bridge id<MTLLibrary>)libRef; + NSString *name = [NSString stringWithUTF8String:funcName]; + return CFBridgingRetain([lib newFunctionWithName:name]); + } +} + +static CFTypeRef newComputePipeline(CFTypeRef devRef, CFTypeRef funcRef) { + @autoreleasepool { + id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef; + id<MTLFunction> func = (__bridge id<MTLFunction>)funcRef; + return CFBridgingRetain([dev newComputePipelineStateWithFunction:func error:nil]); + } +} + +static CFTypeRef newRenderPipeline(CFTypeRef devRef, CFTypeRef vertFunc, CFTypeRef fragFunc, MTLPixelFormat pixelFormat, NSUInteger bufIdx, NSUInteger nverts, MTLVertexFormat *fmts, NSUInteger *offsets, NSUInteger stride, int blend, MTLBlendFactor srcFactor, MTLBlendFactor dstFactor, NSUInteger nvertBufs, NSUInteger nfragBufs) { + @autoreleasepool { + id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef; + id<MTLFunction> vfunc = (__bridge id<MTLFunction>)vertFunc; + id<MTLFunction> ffunc = (__bridge id<MTLFunction>)fragFunc; + MTLVertexDescriptor *vdesc = [MTLVertexDescriptor vertexDescriptor]; + vdesc.layouts[bufIdx].stride = stride; + for (NSUInteger i = 0; i < nverts; i++) { + vdesc.attributes[i].format = fmts[i]; + vdesc.attributes[i].offset = offsets[i]; + vdesc.attributes[i].bufferIndex = bufIdx; + } + MTLRenderPipelineDescriptor *desc = [MTLRenderPipelineDescriptor new]; + desc.vertexFunction = vfunc; + desc.fragmentFunction = ffunc; + desc.vertexDescriptor = vdesc; + for (NSUInteger i = 0; i < nvertBufs; i++) { + if (@available(iOS 11.0, *)) { + desc.vertexBuffers[i].mutability = MTLMutabilityImmutable; + } + } + for (NSUInteger i = 0; i < nfragBufs; i++) { + if (@available(iOS 11.0, *)) { + desc.fragmentBuffers[i].mutability = MTLMutabilityImmutable; + } + } + desc.colorAttachments[0].pixelFormat = pixelFormat; + desc.colorAttachments[0].blendingEnabled = blend ? YES : NO; + desc.colorAttachments[0].sourceAlphaBlendFactor = srcFactor; + desc.colorAttachments[0].sourceRGBBlendFactor = srcFactor; + desc.colorAttachments[0].destinationAlphaBlendFactor = dstFactor; + desc.colorAttachments[0].destinationRGBBlendFactor = dstFactor; + return CFBridgingRetain([dev newRenderPipelineStateWithDescriptor:desc + error:nil]); + } +} +*/ +import "C" + +type Backend struct { + dev C.CFTypeRef + queue C.CFTypeRef + pixelFmt C.MTLPixelFormat + + cmdBuffer C.CFTypeRef + lastCmdBuffer C.CFTypeRef + renderEnc C.CFTypeRef + computeEnc C.CFTypeRef + blitEnc C.CFTypeRef + + prog *Program + topology C.MTLPrimitiveType + + stagingBuf C.CFTypeRef + stagingOff int + + indexBuf *Buffer + + // bufSizes is scratch space for filling out the spvBufferSizeConstants + // that spirv-cross generates for emulating buffer.length expressions in + // shaders. + bufSizes []uint32 +} + +type Texture struct { + backend *Backend + texture C.CFTypeRef + sampler C.CFTypeRef + width int + height int + foreign bool +} + +type Shader struct { + function C.CFTypeRef + inputs []shader.InputLocation +} + +type Program struct { + pipeline C.CFTypeRef + groupSize [3]int +} + +type Pipeline struct { + pipeline C.CFTypeRef + topology C.MTLPrimitiveType +} + +type Buffer struct { + backend *Backend + size int + buffer C.CFTypeRef + + // store is the buffer contents For buffers not allocated on the GPU. + store []byte +} + +const ( + uniformBufferIndex = 0 + attributeBufferIndex = 1 + + spvBufferSizeConstantsBinding = 25 +) + +const ( + texUnits = 4 + bufferUnits = 4 +) + +func init() { + driver.NewMetalDevice = newMetalDevice +} + +func newMetalDevice(api driver.Metal) (driver.Device, error) { + dev := C.CFTypeRef(api.Device) + C.CFRetain(dev) + queue := C.CFTypeRef(api.Queue) + C.CFRetain(queue) + b := &Backend{ + dev: dev, + queue: queue, + pixelFmt: C.MTLPixelFormat(api.PixelFormat), + bufSizes: make([]uint32, bufferUnits), + } + return b, nil +} + +func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Texture { + if b.lastCmdBuffer != 0 { + C.cmdBufferWaitUntilCompleted(b.lastCmdBuffer) + b.stagingOff = 0 + } + if target == nil { + return nil + } + switch t := target.(type) { + case driver.MetalRenderTarget: + texture := C.CFTypeRef(t.Texture) + return &Texture{texture: texture, foreign: true} + case *Texture: + return t + default: + panic(fmt.Sprintf("metal: unsupported render target type: %T", t)) + } +} + +func (b *Backend) startBlit() C.CFTypeRef { + if b.blitEnc != 0 { + return b.blitEnc + } + b.endEncoder() + b.ensureCmdBuffer() + b.blitEnc = C.cmdBufferBlitEncoder(b.cmdBuffer) + if b.blitEnc == 0 { + panic("metal: [MTLCommandBuffer blitCommandEncoder:] failed") + } + return b.blitEnc +} + +func (b *Backend) CopyTexture(dst driver.Texture, dorig image.Point, src driver.Texture, srect image.Rectangle) { + enc := b.startBlit() + dstTex := dst.(*Texture).texture + srcTex := src.(*Texture).texture + ssz := srect.Size() + C.blitEncCopyFromTexture( + enc, + srcTex, + C.MTLOrigin{ + x: C.NSUInteger(srect.Min.X), + y: C.NSUInteger(srect.Min.Y), + }, + C.MTLSize{ + width: C.NSUInteger(ssz.X), + height: C.NSUInteger(ssz.Y), + depth: 1, + }, + dstTex, + C.MTLOrigin{ + x: C.NSUInteger(dorig.X), + y: C.NSUInteger(dorig.Y), + }, + ) +} + +func (b *Backend) EndFrame() { + b.endCmdBuffer(false) +} + +func (b *Backend) endCmdBuffer(wait bool) { + b.endEncoder() + if b.cmdBuffer == 0 { + return + } + C.cmdBufferCommit(b.cmdBuffer) + if wait { + C.cmdBufferWaitUntilCompleted(b.cmdBuffer) + } + if b.lastCmdBuffer != 0 { + C.CFRelease(b.lastCmdBuffer) + } + b.lastCmdBuffer = b.cmdBuffer + b.cmdBuffer = 0 +} + +func (b *Backend) Caps() driver.Caps { + return driver.Caps{ + MaxTextureSize: 8192, + Features: driver.FeatureSRGB | driver.FeatureCompute | driver.FeatureFloatRenderTargets, + } +} + +func (b *Backend) NewTimer() driver.Timer { + panic("timers not supported") +} + +func (b *Backend) IsTimeContinuous() bool { + panic("timers not supported") +} + +func (b *Backend) Release() { + if b.cmdBuffer != 0 { + C.CFRelease(b.cmdBuffer) + } + if b.lastCmdBuffer != 0 { + C.CFRelease(b.lastCmdBuffer) + } + if b.stagingBuf != 0 { + C.CFRelease(b.stagingBuf) + } + C.CFRelease(b.queue) + C.CFRelease(b.dev) + *b = Backend{} +} + +func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, minFilter, magFilter driver.TextureFilter, bindings driver.BufferBinding) (driver.Texture, error) { + mformat := pixelFormatFor(format) + var usage C.MTLTextureUsage + if bindings&(driver.BufferBindingTexture|driver.BufferBindingShaderStorageRead) != 0 { + usage |= C.MTLTextureUsageShaderRead + } + if bindings&driver.BufferBindingFramebuffer != 0 { + usage |= C.MTLTextureUsageRenderTarget + } + if bindings&driver.BufferBindingShaderStorageWrite != 0 { + usage |= C.MTLTextureUsageShaderWrite + } + tex := C.newTexture(b.dev, C.NSUInteger(width), C.NSUInteger(height), mformat, usage) + if tex == 0 { + return nil, errors.New("metal: [MTLDevice newTextureWithDescriptor:] failed") + } + min := samplerFilterFor(minFilter) + max := samplerFilterFor(magFilter) + s := C.newSampler(b.dev, min, max) + if s == 0 { + C.CFRelease(tex) + return nil, errors.New("metal: [MTLDevice newSamplerStateWithDescriptor:] failed") + } + return &Texture{backend: b, texture: tex, sampler: s, width: width, height: height}, nil +} + +func samplerFilterFor(f driver.TextureFilter) C.MTLSamplerMinMagFilter { + switch f { + case driver.FilterNearest: + return C.MTLSamplerMinMagFilterNearest + case driver.FilterLinear: + return C.MTLSamplerMinMagFilterLinear + default: + panic("invalid texture filter") + } +} + +func (b *Backend) NewPipeline(desc driver.PipelineDesc) (driver.Pipeline, error) { + vsh, fsh := desc.VertexShader.(*Shader), desc.FragmentShader.(*Shader) + layout := desc.VertexLayout.Inputs + if got, exp := len(layout), len(vsh.inputs); got != exp { + return nil, fmt.Errorf("metal: number of input descriptors (%d) doesn't match number of inputs (%d)", got, exp) + } + formats := make([]C.MTLVertexFormat, len(layout)) + offsets := make([]C.NSUInteger, len(layout)) + for i, inp := range layout { + index := vsh.inputs[i].Location + formats[index] = vertFormatFor(vsh.inputs[i]) + offsets[index] = C.NSUInteger(inp.Offset) + } + var ( + fmtPtr *C.MTLVertexFormat + offPtr *C.NSUInteger + ) + if len(layout) > 0 { + fmtPtr = &formats[0] + offPtr = &offsets[0] + } + srcFactor := blendFactorFor(desc.BlendDesc.SrcFactor) + dstFactor := blendFactorFor(desc.BlendDesc.DstFactor) + blend := C.int(0) + if desc.BlendDesc.Enable { + blend = 1 + } + pf := b.pixelFmt + if f := desc.PixelFormat; f != driver.TextureFormatOutput { + pf = pixelFormatFor(f) + } + pipe := C.newRenderPipeline( + b.dev, + vsh.function, + fsh.function, + pf, + attributeBufferIndex, + C.NSUInteger(len(layout)), fmtPtr, offPtr, + C.NSUInteger(desc.VertexLayout.Stride), + blend, srcFactor, dstFactor, + 2, // Number of vertex buffers. + 1, // Number of fragment buffers. + ) + if pipe == 0 { + return nil, errors.New("metal: pipeline construction failed") + } + return &Pipeline{pipeline: pipe, topology: primitiveFor(desc.Topology)}, nil +} + +func dataTypeSize(d shader.DataType) int { + switch d { + case shader.DataTypeFloat: + return 4 + default: + panic("unsupported data type") + } +} + +func blendFactorFor(f driver.BlendFactor) C.MTLBlendFactor { + switch f { + case driver.BlendFactorZero: + return C.MTLBlendFactorZero + case driver.BlendFactorOne: + return C.MTLBlendFactorOne + case driver.BlendFactorOneMinusSrcAlpha: + return C.MTLBlendFactorOneMinusSourceAlpha + case driver.BlendFactorDstColor: + return C.MTLBlendFactorDestinationColor + default: + panic("unsupported blend factor") + } +} + +func vertFormatFor(f shader.InputLocation) C.MTLVertexFormat { + t := f.Type + s := f.Size + switch { + case t == shader.DataTypeFloat && s == 1: + return C.MTLVertexFormatFloat + case t == shader.DataTypeFloat && s == 2: + return C.MTLVertexFormatFloat2 + case t == shader.DataTypeFloat && s == 3: + return C.MTLVertexFormatFloat3 + case t == shader.DataTypeFloat && s == 4: + return C.MTLVertexFormatFloat4 + default: + panic("unsupported data type") + } +} + +func pixelFormatFor(f driver.TextureFormat) C.MTLPixelFormat { + switch f { + case driver.TextureFormatFloat: + return C.MTLPixelFormatR16Float + case driver.TextureFormatRGBA8: + return C.MTLPixelFormatRGBA8Unorm + case driver.TextureFormatSRGBA: + return C.MTLPixelFormatRGBA8Unorm_sRGB + default: + panic("unsupported pixel format") + } +} + +func (b *Backend) NewBuffer(typ driver.BufferBinding, size int) (driver.Buffer, error) { + // Transfer buffer contents in command encoders on every use for + // smaller buffers. The advantage is that buffer re-use during a frame + // won't occur a GPU wait. + // We can't do this for buffers written to by the GPU and read by the client, + // and Metal doesn't require a buffer for indexed draws. + if size <= 4096 && typ&(driver.BufferBindingShaderStorageWrite|driver.BufferBindingIndices) == 0 { + return &Buffer{size: size, store: make([]byte, size)}, nil + } + buf := C.newBuffer(b.dev, C.NSUInteger(size), C.MTLResourceStorageModePrivate) + return &Buffer{backend: b, size: size, buffer: buf}, nil +} + +func (b *Backend) NewImmutableBuffer(typ driver.BufferBinding, data []byte) (driver.Buffer, error) { + buf, err := b.NewBuffer(typ, len(data)) + if err != nil { + return nil, err + } + buf.Upload(data) + return buf, nil +} + +func (b *Backend) NewComputeProgram(src shader.Sources) (driver.Program, error) { + sh, err := b.newShader(src) + if err != nil { + return nil, err + } + defer sh.Release() + pipe := C.newComputePipeline(b.dev, sh.function) + if pipe == 0 { + return nil, fmt.Errorf("metal: compute program %q load failed", src.Name) + } + return &Program{pipeline: pipe, groupSize: src.WorkgroupSize}, nil +} + +func (b *Backend) NewVertexShader(src shader.Sources) (driver.VertexShader, error) { + return b.newShader(src) +} + +func (b *Backend) NewFragmentShader(src shader.Sources) (driver.FragmentShader, error) { + return b.newShader(src) +} + +func (b *Backend) newShader(src shader.Sources) (*Shader, error) { + vsrc := []byte(src.MetalLib) + cname := C.CString(src.Name) + defer C.free(unsafe.Pointer(cname)) + vlib := C.newLibrary(b.dev, cname, unsafe.Pointer(&vsrc[0]), C.size_t(len(vsrc))) + if vlib == 0 { + return nil, fmt.Errorf("metal: vertex shader %q load failed", src.Name) + } + defer C.CFRelease(vlib) + funcName := C.CString("main0") + defer C.free(unsafe.Pointer(funcName)) + f := C.libraryNewFunction(vlib, funcName) + if f == 0 { + return nil, fmt.Errorf("metal: main function not found in %q", src.Name) + } + return &Shader{function: f, inputs: src.Inputs}, nil +} + +func (b *Backend) Viewport(x, y, width, height int) { + enc := b.renderEnc + if enc == 0 { + panic("no active render pass") + } + C.renderEncViewport(enc, C.MTLViewport{ + originX: C.double(x), + originY: C.double(y), + width: C.double(width), + height: C.double(height), + znear: 0.0, + zfar: 1.0, + }) +} + +func (b *Backend) DrawArrays(off, count int) { + enc := b.renderEnc + if enc == 0 { + panic("no active render pass") + } + C.renderEncDrawPrimitives(enc, b.topology, C.NSUInteger(off), C.NSUInteger(count)) +} + +func (b *Backend) DrawElements(off, count int) { + enc := b.renderEnc + if enc == 0 { + panic("no active render pass") + } + C.renderEncDrawIndexedPrimitives(enc, b.topology, b.indexBuf.buffer, C.NSUInteger(off), C.NSUInteger(count)) +} + +func primitiveFor(mode driver.Topology) C.MTLPrimitiveType { + switch mode { + case driver.TopologyTriangles: + return C.MTLPrimitiveTypeTriangle + case driver.TopologyTriangleStrip: + return C.MTLPrimitiveTypeTriangleStrip + default: + panic("metal: unknown draw mode") + } +} + +func (b *Backend) BindImageTexture(unit int, tex driver.Texture) { + b.BindTexture(unit, tex) +} + +func (b *Backend) BeginCompute() { + b.endEncoder() + b.ensureCmdBuffer() + for i := range b.bufSizes { + b.bufSizes[i] = 0 + } + b.computeEnc = C.cmdBufferComputeEncoder(b.cmdBuffer) + if b.computeEnc == 0 { + panic("metal: [MTLCommandBuffer computeCommandEncoder:] failed") + } +} + +func (b *Backend) EndCompute() { + if b.computeEnc == 0 { + panic("no active compute pass") + } + C.computeEncEnd(b.computeEnc) + C.CFRelease(b.computeEnc) + b.computeEnc = 0 +} + +func (b *Backend) DispatchCompute(x, y, z int) { + enc := b.computeEnc + if enc == 0 { + panic("no active compute pass") + } + C.computeEncSetBytes(enc, unsafe.Pointer(&b.bufSizes[0]), C.NSUInteger(len(b.bufSizes)*4), spvBufferSizeConstantsBinding) + threadgroupsPerGrid := C.MTLSize{ + width: C.NSUInteger(x), height: C.NSUInteger(y), depth: C.NSUInteger(z), + } + sz := b.prog.groupSize + threadsPerThreadgroup := C.MTLSize{ + width: C.NSUInteger(sz[0]), height: C.NSUInteger(sz[1]), depth: C.NSUInteger(sz[2]), + } + C.computeEncDispatch(enc, threadgroupsPerGrid, threadsPerThreadgroup) +} + +func (b *Backend) stagingBuffer(size int) (C.CFTypeRef, int) { + if b.stagingBuf == 0 || b.stagingOff+size > len(bufferStore(b.stagingBuf)) { + if b.stagingBuf != 0 { + C.CFRelease(b.stagingBuf) + } + cap := 2 * (b.stagingOff + size) + b.stagingBuf = C.newBuffer(b.dev, C.NSUInteger(cap), C.MTLResourceStorageModeShared|C.MTLResourceCPUCacheModeWriteCombined) + if b.stagingBuf == 0 { + panic(fmt.Errorf("metal: failed to allocate %d bytes of staging buffer", cap)) + } + b.stagingOff = 0 + } + off := b.stagingOff + b.stagingOff += size + return b.stagingBuf, off +} + +func (t *Texture) Upload(offset, size image.Point, pixels []byte, stride int) { + if len(pixels) == 0 { + return + } + if stride == 0 { + stride = size.X * 4 + } + dstStride := size.X * 4 + n := size.Y * dstStride + buf, off := t.backend.stagingBuffer(n) + store := bufferSlice(buf, off, n) + var srcOff, dstOff int + for y := 0; y < size.Y; y++ { + srcRow := pixels[srcOff : srcOff+dstStride] + dstRow := store[dstOff : dstOff+dstStride] + copy(dstRow, srcRow) + dstOff += dstStride + srcOff += stride + } + enc := t.backend.startBlit() + orig := C.MTLOrigin{ + x: C.NSUInteger(offset.X), + y: C.NSUInteger(offset.Y), + } + msize := C.MTLSize{ + width: C.NSUInteger(size.X), + height: C.NSUInteger(size.Y), + depth: 1, + } + C.blitEncCopyBufferToTexture(enc, buf, t.texture, C.NSUInteger(off), C.NSUInteger(dstStride), C.NSUInteger(len(store)), msize, orig) +} + +func (t *Texture) Release() { + if t.foreign { + panic("metal: release of external texture") + } + C.CFRelease(t.texture) + C.CFRelease(t.sampler) + *t = Texture{} +} + +func (p *Pipeline) Release() { + C.CFRelease(p.pipeline) + *p = Pipeline{} +} + +func (b *Backend) PrepareTexture(tex driver.Texture) {} + +func (b *Backend) BindTexture(unit int, tex driver.Texture) { + t := tex.(*Texture) + if enc := b.renderEnc; enc != 0 { + C.renderEncSetFragmentTexture(enc, C.NSUInteger(unit), t.texture) + C.renderEncSetFragmentSamplerState(enc, C.NSUInteger(unit), t.sampler) + } else if enc := b.computeEnc; enc != 0 { + C.computeEncSetTexture(enc, C.NSUInteger(unit), t.texture) + } else { + panic("no active render nor compute pass") + } +} + +func (b *Backend) ensureCmdBuffer() { + if b.cmdBuffer != 0 { + return + } + b.cmdBuffer = C.queueNewBuffer(b.queue) + if b.cmdBuffer == 0 { + panic("metal: [MTLCommandQueue cmdBuffer] failed") + } +} + +func (b *Backend) BindPipeline(pipe driver.Pipeline) { + p := pipe.(*Pipeline) + enc := b.renderEnc + if enc == 0 { + panic("no active render pass") + } + C.renderEncSetRenderPipelineState(enc, p.pipeline) + b.topology = p.topology +} + +func (b *Backend) BindProgram(prog driver.Program) { + enc := b.computeEnc + if enc == 0 { + panic("no active compute pass") + } + p := prog.(*Program) + C.computeEncSetPipeline(enc, p.pipeline) + b.prog = p +} + +func (s *Shader) Release() { + C.CFRelease(s.function) + *s = Shader{} +} + +func (p *Program) Release() { + C.CFRelease(p.pipeline) + *p = Program{} +} + +func (b *Backend) BindStorageBuffer(binding int, buffer driver.Buffer) { + buf := buffer.(*Buffer) + b.bufSizes[binding] = uint32(buf.size) + enc := b.computeEnc + if enc == 0 { + panic("no active compute pass") + } + if buf.buffer != 0 { + C.computeEncSetBuffer(enc, C.NSUInteger(binding), buf.buffer) + } else if buf.size > 0 { + C.computeEncSetBytes(enc, unsafe.Pointer(&buf.store[0]), C.NSUInteger(buf.size), C.NSUInteger(binding)) + } +} + +func (b *Backend) BindUniforms(buf driver.Buffer) { + bf := buf.(*Buffer) + enc := b.renderEnc + if enc == 0 { + panic("no active render pass") + } + if bf.buffer != 0 { + C.renderEncSetVertexBuffer(enc, bf.buffer, uniformBufferIndex, 0) + C.renderEncSetFragmentBuffer(enc, bf.buffer, uniformBufferIndex, 0) + } else if bf.size > 0 { + C.renderEncSetVertexBytes(enc, unsafe.Pointer(&bf.store[0]), C.NSUInteger(bf.size), uniformBufferIndex) + C.renderEncSetFragmentBytes(enc, unsafe.Pointer(&bf.store[0]), C.NSUInteger(bf.size), uniformBufferIndex) + } +} + +func (b *Backend) BindVertexBuffer(buf driver.Buffer, offset int) { + bf := buf.(*Buffer) + enc := b.renderEnc + if enc == 0 { + panic("no active render pass") + } + if bf.buffer != 0 { + C.renderEncSetVertexBuffer(enc, bf.buffer, attributeBufferIndex, C.NSUInteger(offset)) + } else if n := bf.size - offset; n > 0 { + C.renderEncSetVertexBytes(enc, unsafe.Pointer(&bf.store[offset]), C.NSUInteger(n), attributeBufferIndex) + } +} + +func (b *Backend) BindIndexBuffer(buf driver.Buffer) { + b.indexBuf = buf.(*Buffer) +} + +func (b *Buffer) Download(data []byte) error { + if len(data) > b.size { + panic(fmt.Errorf("len(data) (%d) larger than len(content) (%d)", len(data), b.size)) + } + buf, off := b.backend.stagingBuffer(len(data)) + enc := b.backend.startBlit() + C.blitEncCopyBufferToBuffer(enc, b.buffer, buf, 0, C.NSUInteger(off), C.NSUInteger(len(data))) + b.backend.endCmdBuffer(true) + store := bufferSlice(buf, off, len(data)) + copy(data, store) + return nil +} + +func (b *Buffer) Upload(data []byte) { + if len(data) > b.size { + panic(fmt.Errorf("len(data) (%d) larger than len(content) (%d)", len(data), b.size)) + } + if b.buffer == 0 { + copy(b.store, data) + return + } + buf, off := b.backend.stagingBuffer(len(data)) + store := bufferSlice(buf, off, len(data)) + copy(store, data) + enc := b.backend.startBlit() + C.blitEncCopyBufferToBuffer(enc, buf, b.buffer, C.NSUInteger(off), 0, C.NSUInteger(len(store))) +} + +func bufferStore(buf C.CFTypeRef) []byte { + contents := C.bufferContents(buf) + return (*(*[1 << 30]byte)(contents.addr))[:contents.size:contents.size] +} + +func bufferSlice(buf C.CFTypeRef, off, len int) []byte { + store := bufferStore(buf) + return store[off : off+len] +} + +func (b *Buffer) Release() { + if b.buffer != 0 { + C.CFRelease(b.buffer) + } + *b = Buffer{} +} + +func (t *Texture) ReadPixels(src image.Rectangle, pixels []byte, stride int) error { + if len(pixels) == 0 { + return nil + } + sz := src.Size() + orig := C.MTLOrigin{ + x: C.NSUInteger(src.Min.X), + y: C.NSUInteger(src.Min.Y), + } + msize := C.MTLSize{ + width: C.NSUInteger(sz.X), + height: C.NSUInteger(sz.Y), + depth: 1, + } + stageStride := sz.X * 4 + n := sz.Y * stageStride + buf, off := t.backend.stagingBuffer(n) + enc := t.backend.startBlit() + C.blitEncCopyTextureToBuffer(enc, t.texture, buf, C.NSUInteger(off), C.NSUInteger(stageStride), C.NSUInteger(n), msize, orig) + t.backend.endCmdBuffer(true) + store := bufferSlice(buf, off, n) + var srcOff, dstOff int + for y := 0; y < sz.Y; y++ { + dstRow := pixels[srcOff : srcOff+stageStride] + srcRow := store[dstOff : dstOff+stageStride] + copy(dstRow, srcRow) + dstOff += stageStride + srcOff += stride + } + return nil +} + +func (b *Backend) BeginRenderPass(tex driver.Texture, d driver.LoadDesc) { + b.endEncoder() + b.ensureCmdBuffer() + f := tex.(*Texture) + col := d.ClearColor + var act C.MTLLoadAction + switch d.Action { + case driver.LoadActionKeep: + act = C.MTLLoadActionLoad + case driver.LoadActionClear: + act = C.MTLLoadActionClear + case driver.LoadActionInvalidate: + act = C.MTLLoadActionDontCare + } + b.renderEnc = C.cmdBufferRenderEncoder(b.cmdBuffer, f.texture, act, C.float(col.R), C.float(col.G), C.float(col.B), C.float(col.A)) + if b.renderEnc == 0 { + panic("metal: [MTLCommandBuffer renderCommandEncoderWithDescriptor:] failed") + } +} + +func (b *Backend) EndRenderPass() { + if b.renderEnc == 0 { + panic("no active render pass") + } + C.renderEncEnd(b.renderEnc) + C.CFRelease(b.renderEnc) + b.renderEnc = 0 +} + +func (b *Backend) endEncoder() { + if b.renderEnc != 0 { + panic("active render pass") + } + if b.computeEnc != 0 { + panic("active compute pass") + } + if b.blitEnc != 0 { + C.blitEncEnd(b.blitEnc) + C.CFRelease(b.blitEnc) + b.blitEnc = 0 + } +} + +func (f *Texture) ImplementsRenderTarget() {} diff --git a/vendor/gioui.org/gpu/internal/opengl/opengl.go b/vendor/gioui.org/gpu/internal/opengl/opengl.go new file mode 100644 index 0000000..ef89197 --- /dev/null +++ b/vendor/gioui.org/gpu/internal/opengl/opengl.go @@ -0,0 +1,1357 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package opengl + +import ( + "errors" + "fmt" + "image" + "strings" + "time" + "unsafe" + + "gioui.org/gpu/internal/driver" + "gioui.org/internal/gl" + "gioui.org/shader" +) + +// Backend implements driver.Device. +type Backend struct { + funcs *gl.Functions + + clear bool + glstate glState + state state + savedState glState + sharedCtx bool + + glver [2]int + gles bool + feats driver.Caps + // floatTriple holds the settings for floating point + // textures. + floatTriple textureTriple + // Single channel alpha textures. + alphaTriple textureTriple + srgbaTriple textureTriple + storage [storageBindings]*buffer + + outputFBO gl.Framebuffer + sRGBFBO *SRGBFBO + + // vertArray is bound during a frame. We don't need it, but + // core desktop OpenGL profile 3.3 requires some array bound. + vertArray gl.VertexArray +} + +// State tracking. +type glState struct { + drawFBO gl.Framebuffer + readFBO gl.Framebuffer + renderBuf gl.Renderbuffer + vertAttribs [5]struct { + obj gl.Buffer + enabled bool + size int + typ gl.Enum + normalized bool + stride int + offset uintptr + } + prog gl.Program + texUnits struct { + active gl.Enum + binds [2]gl.Texture + } + arrayBuf gl.Buffer + elemBuf gl.Buffer + uniBuf gl.Buffer + uniBufs [2]gl.Buffer + storeBuf gl.Buffer + storeBufs [4]gl.Buffer + vertArray gl.VertexArray + srgb bool + blend struct { + enable bool + srcRGB, dstRGB gl.Enum + srcA, dstA gl.Enum + } + clearColor [4]float32 + viewport [4]int + unpack_row_length int + pack_row_length int +} + +type state struct { + pipeline *pipeline + buffer bufferBinding +} + +type bufferBinding struct { + obj gl.Buffer + offset int +} + +type timer struct { + funcs *gl.Functions + obj gl.Query +} + +type texture struct { + backend *Backend + obj gl.Texture + fbo gl.Framebuffer + hasFBO bool + triple textureTriple + width int + height int + bindings driver.BufferBinding + foreign bool +} + +type pipeline struct { + prog *program + inputs []shader.InputLocation + layout driver.VertexLayout + blend driver.BlendDesc + topology driver.Topology +} + +type buffer struct { + backend *Backend + hasBuffer bool + obj gl.Buffer + typ driver.BufferBinding + size int + immutable bool + // For emulation of uniform buffers. + data []byte +} + +type glshader struct { + backend *Backend + obj gl.Shader + src shader.Sources +} + +type program struct { + backend *Backend + obj gl.Program + vertUniforms uniforms + fragUniforms uniforms +} + +type uniforms struct { + locs []uniformLocation + size int +} + +type uniformLocation struct { + uniform gl.Uniform + offset int + typ shader.DataType + size int +} + +type inputLayout struct { + inputs []shader.InputLocation + layout []driver.InputDesc +} + +// textureTriple holds the type settings for +// a TexImage2D call. +type textureTriple struct { + internalFormat gl.Enum + format gl.Enum + typ gl.Enum +} + +const ( + storageBindings = 32 +) + +func init() { + driver.NewOpenGLDevice = newOpenGLDevice +} + +// Supporting compute programs is theoretically possible with OpenGL ES 3.1. In +// practice, there are too many driver issues, especially on Android (e.g. +// Google Pixel, Samsung J2 are both broken i different ways). Disable support +// and rely on Vulkan for devices that support it, and the CPU fallback for +// devices that don't. +const brokenGLES31 = true + +func newOpenGLDevice(api driver.OpenGL) (driver.Device, error) { + f, err := gl.NewFunctions(api.Context, api.ES) + if err != nil { + return nil, err + } + exts := strings.Split(f.GetString(gl.EXTENSIONS), " ") + glVer := f.GetString(gl.VERSION) + ver, gles, err := gl.ParseGLVersion(glVer) + if err != nil { + return nil, err + } + floatTriple, ffboErr := floatTripleFor(f, ver, exts) + srgbaTriple, srgbErr := srgbaTripleFor(ver, exts) + gles31 := gles && (ver[0] > 3 || (ver[0] == 3 && ver[1] >= 1)) + b := &Backend{ + glver: ver, + gles: gles, + funcs: f, + floatTriple: floatTriple, + alphaTriple: alphaTripleFor(ver), + srgbaTriple: srgbaTriple, + sharedCtx: api.Shared, + } + b.feats.BottomLeftOrigin = true + if srgbErr == nil { + b.feats.Features |= driver.FeatureSRGB + } + if ffboErr == nil { + b.feats.Features |= driver.FeatureFloatRenderTargets + } + if gles31 && !brokenGLES31 { + b.feats.Features |= driver.FeatureCompute + } + if hasExtension(exts, "GL_EXT_disjoint_timer_query_webgl2") || hasExtension(exts, "GL_EXT_disjoint_timer_query") { + b.feats.Features |= driver.FeatureTimers + } + b.feats.MaxTextureSize = f.GetInteger(gl.MAX_TEXTURE_SIZE) + if !b.sharedCtx { + // We have exclusive access to the context, so query the GL state once + // instead of at each frame. + b.glstate = b.queryState() + } + return b, nil +} + +func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Texture { + b.clear = clear + if b.sharedCtx { + b.glstate = b.queryState() + b.savedState = b.glstate + } + b.state = state{} + var renderFBO gl.Framebuffer + if target != nil { + switch t := target.(type) { + case driver.OpenGLRenderTarget: + renderFBO = gl.Framebuffer(t) + case *texture: + renderFBO = t.ensureFBO() + default: + panic(fmt.Errorf("opengl: invalid render target type: %T", target)) + } + } + b.outputFBO = renderFBO + b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, renderFBO) + if b.gles { + // If the output framebuffer is not in the sRGB colorspace already, emulate it. + var fbEncoding int + if !renderFBO.Valid() { + fbEncoding = b.funcs.GetFramebufferAttachmentParameteri(gl.FRAMEBUFFER, gl.BACK, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING) + } else { + fbEncoding = b.funcs.GetFramebufferAttachmentParameteri(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING) + } + if fbEncoding == gl.LINEAR && viewport != (image.Point{}) { + if b.sRGBFBO == nil { + sfbo, err := NewSRGBFBO(b.funcs, &b.glstate) + if err != nil { + panic(err) + } + b.sRGBFBO = sfbo + } + if err := b.sRGBFBO.Refresh(viewport); err != nil { + panic(err) + } + renderFBO = b.sRGBFBO.Framebuffer() + } else if b.sRGBFBO != nil { + b.sRGBFBO.Release() + b.sRGBFBO = nil + } + } else { + b.glstate.set(b.funcs, gl.FRAMEBUFFER_SRGB, true) + if !b.vertArray.Valid() { + b.vertArray = b.funcs.CreateVertexArray() + } + b.glstate.bindVertexArray(b.funcs, b.vertArray) + } + b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, renderFBO) + if b.sRGBFBO != nil && !clear { + b.clearOutput(0, 0, 0, 0) + } + return &texture{backend: b, fbo: renderFBO, hasFBO: true, foreign: true} +} + +func (b *Backend) EndFrame() { + if b.sRGBFBO != nil { + b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, b.outputFBO) + if b.clear { + b.SetBlend(false) + } else { + b.BlendFunc(driver.BlendFactorOne, driver.BlendFactorOneMinusSrcAlpha) + b.SetBlend(true) + } + b.sRGBFBO.Blit() + } + if b.sharedCtx { + b.restoreState(b.savedState) + } +} + +func (b *Backend) queryState() glState { + s := glState{ + prog: gl.Program(b.funcs.GetBinding(gl.CURRENT_PROGRAM)), + arrayBuf: gl.Buffer(b.funcs.GetBinding(gl.ARRAY_BUFFER_BINDING)), + elemBuf: gl.Buffer(b.funcs.GetBinding(gl.ELEMENT_ARRAY_BUFFER_BINDING)), + drawFBO: gl.Framebuffer(b.funcs.GetBinding(gl.FRAMEBUFFER_BINDING)), + clearColor: b.funcs.GetFloat4(gl.COLOR_CLEAR_VALUE), + viewport: b.funcs.GetInteger4(gl.VIEWPORT), + unpack_row_length: b.funcs.GetInteger(gl.UNPACK_ROW_LENGTH), + pack_row_length: b.funcs.GetInteger(gl.PACK_ROW_LENGTH), + } + s.blend.enable = b.funcs.IsEnabled(gl.BLEND) + s.blend.srcRGB = gl.Enum(b.funcs.GetInteger(gl.BLEND_SRC_RGB)) + s.blend.dstRGB = gl.Enum(b.funcs.GetInteger(gl.BLEND_DST_RGB)) + s.blend.srcA = gl.Enum(b.funcs.GetInteger(gl.BLEND_SRC_ALPHA)) + s.blend.dstA = gl.Enum(b.funcs.GetInteger(gl.BLEND_DST_ALPHA)) + s.texUnits.active = gl.Enum(b.funcs.GetInteger(gl.ACTIVE_TEXTURE)) + if !b.gles { + s.srgb = b.funcs.IsEnabled(gl.FRAMEBUFFER_SRGB) + } + if !b.gles || b.glver[0] >= 3 { + s.vertArray = gl.VertexArray(b.funcs.GetBinding(gl.VERTEX_ARRAY_BINDING)) + s.readFBO = gl.Framebuffer(b.funcs.GetBinding(gl.READ_FRAMEBUFFER_BINDING)) + s.uniBuf = gl.Buffer(b.funcs.GetBinding(gl.UNIFORM_BUFFER_BINDING)) + for i := range s.uniBufs { + s.uniBufs[i] = gl.Buffer(b.funcs.GetBindingi(gl.UNIFORM_BUFFER_BINDING, i)) + } + } + if b.gles && (b.glver[0] > 3 || (b.glver[0] == 3 && b.glver[1] >= 1)) { + s.storeBuf = gl.Buffer(b.funcs.GetBinding(gl.SHADER_STORAGE_BUFFER_BINDING)) + for i := range s.storeBufs { + s.storeBufs[i] = gl.Buffer(b.funcs.GetBindingi(gl.SHADER_STORAGE_BUFFER_BINDING, i)) + } + } + for i := range s.texUnits.binds { + s.activeTexture(b.funcs, gl.TEXTURE0+gl.Enum(i)) + s.texUnits.binds[i] = gl.Texture(b.funcs.GetBinding(gl.TEXTURE_BINDING_2D)) + } + for i := range s.vertAttribs { + a := &s.vertAttribs[i] + a.enabled = b.funcs.GetVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_ENABLED) != gl.FALSE + a.obj = gl.Buffer(b.funcs.GetVertexAttribBinding(i, gl.VERTEX_ATTRIB_ARRAY_ENABLED)) + a.size = b.funcs.GetVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_SIZE) + a.typ = gl.Enum(b.funcs.GetVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_TYPE)) + a.normalized = b.funcs.GetVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_NORMALIZED) != gl.FALSE + a.stride = b.funcs.GetVertexAttrib(i, gl.VERTEX_ATTRIB_ARRAY_STRIDE) + a.offset = b.funcs.GetVertexAttribPointer(i, gl.VERTEX_ATTRIB_ARRAY_POINTER) + } + return s +} + +func (b *Backend) restoreState(dst glState) { + src := b.glstate + f := b.funcs + for i, unit := range dst.texUnits.binds { + src.bindTexture(f, i, unit) + } + src.activeTexture(f, dst.texUnits.active) + src.bindFramebuffer(f, gl.FRAMEBUFFER, dst.drawFBO) + src.bindFramebuffer(f, gl.READ_FRAMEBUFFER, dst.readFBO) + src.set(f, gl.BLEND, dst.blend.enable) + bf := dst.blend + src.setBlendFuncSeparate(f, bf.srcRGB, bf.dstRGB, bf.srcA, bf.dstA) + src.set(f, gl.FRAMEBUFFER_SRGB, dst.srgb) + src.bindVertexArray(f, dst.vertArray) + src.useProgram(f, dst.prog) + src.bindBuffer(f, gl.ELEMENT_ARRAY_BUFFER, dst.elemBuf) + for i, b := range dst.uniBufs { + src.bindBufferBase(f, gl.UNIFORM_BUFFER, i, b) + } + src.bindBuffer(f, gl.UNIFORM_BUFFER, dst.uniBuf) + for i, b := range dst.storeBufs { + src.bindBufferBase(f, gl.SHADER_STORAGE_BUFFER, i, b) + } + src.bindBuffer(f, gl.SHADER_STORAGE_BUFFER, dst.storeBuf) + col := dst.clearColor + src.setClearColor(f, col[0], col[1], col[2], col[3]) + for i, attr := range dst.vertAttribs { + src.setVertexAttribArray(f, i, attr.enabled) + src.vertexAttribPointer(f, attr.obj, i, attr.size, attr.typ, attr.normalized, attr.stride, int(attr.offset)) + } + src.bindBuffer(f, gl.ARRAY_BUFFER, dst.arrayBuf) + v := dst.viewport + src.setViewport(f, v[0], v[1], v[2], v[3]) + src.pixelStorei(f, gl.UNPACK_ROW_LENGTH, dst.unpack_row_length) + src.pixelStorei(f, gl.PACK_ROW_LENGTH, dst.pack_row_length) +} + +func (s *glState) setVertexAttribArray(f *gl.Functions, idx int, enabled bool) { + a := &s.vertAttribs[idx] + if enabled != a.enabled { + if enabled { + f.EnableVertexAttribArray(gl.Attrib(idx)) + } else { + f.DisableVertexAttribArray(gl.Attrib(idx)) + } + a.enabled = enabled + } +} + +func (s *glState) vertexAttribPointer(f *gl.Functions, buf gl.Buffer, idx, size int, typ gl.Enum, normalized bool, stride, offset int) { + s.bindBuffer(f, gl.ARRAY_BUFFER, buf) + a := &s.vertAttribs[idx] + a.obj = buf + a.size = size + a.typ = typ + a.normalized = normalized + a.stride = stride + a.offset = uintptr(offset) + f.VertexAttribPointer(gl.Attrib(idx), a.size, a.typ, a.normalized, a.stride, int(a.offset)) +} + +func (s *glState) activeTexture(f *gl.Functions, unit gl.Enum) { + if unit != s.texUnits.active { + f.ActiveTexture(unit) + s.texUnits.active = unit + } +} + +func (s *glState) bindRenderbuffer(f *gl.Functions, target gl.Enum, r gl.Renderbuffer) { + if !r.Equal(s.renderBuf) { + f.BindRenderbuffer(gl.RENDERBUFFER, r) + s.renderBuf = r + } +} + +func (s *glState) bindTexture(f *gl.Functions, unit int, t gl.Texture) { + s.activeTexture(f, gl.TEXTURE0+gl.Enum(unit)) + if !t.Equal(s.texUnits.binds[unit]) { + f.BindTexture(gl.TEXTURE_2D, t) + s.texUnits.binds[unit] = t + } +} + +func (s *glState) bindVertexArray(f *gl.Functions, a gl.VertexArray) { + if !a.Equal(s.vertArray) { + f.BindVertexArray(a) + s.vertArray = a + } +} + +func (s *glState) deleteRenderbuffer(f *gl.Functions, r gl.Renderbuffer) { + f.DeleteRenderbuffer(r) + if r.Equal(s.renderBuf) { + s.renderBuf = gl.Renderbuffer{} + } +} + +func (s *glState) deleteFramebuffer(f *gl.Functions, fbo gl.Framebuffer) { + f.DeleteFramebuffer(fbo) + if fbo.Equal(s.drawFBO) { + s.drawFBO = gl.Framebuffer{} + } + if fbo.Equal(s.readFBO) { + s.readFBO = gl.Framebuffer{} + } +} + +func (s *glState) deleteBuffer(f *gl.Functions, b gl.Buffer) { + f.DeleteBuffer(b) + if b.Equal(s.arrayBuf) { + s.arrayBuf = gl.Buffer{} + } + if b.Equal(s.elemBuf) { + s.elemBuf = gl.Buffer{} + } + if b.Equal(s.uniBuf) { + s.uniBuf = gl.Buffer{} + } + if b.Equal(s.storeBuf) { + s.uniBuf = gl.Buffer{} + } + for i, b2 := range s.storeBufs { + if b.Equal(b2) { + s.storeBufs[i] = gl.Buffer{} + } + } + for i, b2 := range s.uniBufs { + if b.Equal(b2) { + s.uniBufs[i] = gl.Buffer{} + } + } +} + +func (s *glState) deleteProgram(f *gl.Functions, p gl.Program) { + f.DeleteProgram(p) + if p.Equal(s.prog) { + s.prog = gl.Program{} + } +} + +func (s *glState) deleteVertexArray(f *gl.Functions, a gl.VertexArray) { + f.DeleteVertexArray(a) + if a.Equal(s.vertArray) { + s.vertArray = gl.VertexArray{} + } +} + +func (s *glState) deleteTexture(f *gl.Functions, t gl.Texture) { + f.DeleteTexture(t) + binds := &s.texUnits.binds + for i, obj := range binds { + if t.Equal(obj) { + binds[i] = gl.Texture{} + } + } +} + +func (s *glState) useProgram(f *gl.Functions, p gl.Program) { + if !p.Equal(s.prog) { + f.UseProgram(p) + s.prog = p + } +} + +func (s *glState) bindFramebuffer(f *gl.Functions, target gl.Enum, fbo gl.Framebuffer) { + switch target { + case gl.FRAMEBUFFER: + if fbo.Equal(s.drawFBO) && fbo.Equal(s.readFBO) { + return + } + s.drawFBO = fbo + s.readFBO = fbo + case gl.READ_FRAMEBUFFER: + if fbo.Equal(s.readFBO) { + return + } + s.readFBO = fbo + case gl.DRAW_FRAMEBUFFER: + if fbo.Equal(s.drawFBO) { + return + } + s.drawFBO = fbo + default: + panic("unknown target") + } + f.BindFramebuffer(target, fbo) +} + +func (s *glState) bindBufferBase(f *gl.Functions, target gl.Enum, idx int, buf gl.Buffer) { + switch target { + case gl.UNIFORM_BUFFER: + if buf.Equal(s.uniBuf) && buf.Equal(s.uniBufs[idx]) { + return + } + s.uniBuf = buf + s.uniBufs[idx] = buf + case gl.SHADER_STORAGE_BUFFER: + if buf.Equal(s.storeBuf) && buf.Equal(s.storeBufs[idx]) { + return + } + s.storeBuf = buf + s.storeBufs[idx] = buf + default: + panic("unknown buffer target") + } + f.BindBufferBase(target, idx, buf) +} + +func (s *glState) bindBuffer(f *gl.Functions, target gl.Enum, buf gl.Buffer) { + switch target { + case gl.ARRAY_BUFFER: + if buf.Equal(s.arrayBuf) { + return + } + s.arrayBuf = buf + case gl.ELEMENT_ARRAY_BUFFER: + if buf.Equal(s.elemBuf) { + return + } + s.elemBuf = buf + case gl.UNIFORM_BUFFER: + if buf.Equal(s.uniBuf) { + return + } + s.uniBuf = buf + case gl.SHADER_STORAGE_BUFFER: + if buf.Equal(s.storeBuf) { + return + } + s.storeBuf = buf + default: + panic("unknown buffer target") + } + f.BindBuffer(target, buf) +} + +func (s *glState) pixelStorei(f *gl.Functions, pname gl.Enum, val int) { + switch pname { + case gl.UNPACK_ROW_LENGTH: + if val == s.unpack_row_length { + return + } + s.unpack_row_length = val + case gl.PACK_ROW_LENGTH: + if val == s.pack_row_length { + return + } + s.pack_row_length = val + default: + panic("unsupported PixelStorei pname") + } + f.PixelStorei(pname, val) +} + +func (s *glState) setClearColor(f *gl.Functions, r, g, b, a float32) { + col := [4]float32{r, g, b, a} + if col != s.clearColor { + f.ClearColor(r, g, b, a) + s.clearColor = col + } +} + +func (s *glState) setViewport(f *gl.Functions, x, y, width, height int) { + view := [4]int{x, y, width, height} + if view != s.viewport { + f.Viewport(x, y, width, height) + s.viewport = view + } +} + +func (s *glState) setBlendFuncSeparate(f *gl.Functions, srcRGB, dstRGB, srcA, dstA gl.Enum) { + if srcRGB != s.blend.srcRGB || dstRGB != s.blend.dstRGB || srcA != s.blend.srcA || dstA != s.blend.dstA { + s.blend.srcRGB = srcRGB + s.blend.dstRGB = dstRGB + s.blend.srcA = srcA + s.blend.dstA = dstA + f.BlendFuncSeparate(srcA, dstA, srcA, dstA) + } +} + +func (s *glState) set(f *gl.Functions, target gl.Enum, enable bool) { + switch target { + case gl.FRAMEBUFFER_SRGB: + if s.srgb == enable { + return + } + s.srgb = enable + case gl.BLEND: + if enable == s.blend.enable { + return + } + s.blend.enable = enable + default: + panic("unknown enable") + } + if enable { + f.Enable(target) + } else { + f.Disable(target) + } +} + +func (b *Backend) Caps() driver.Caps { + return b.feats +} + +func (b *Backend) NewTimer() driver.Timer { + return &timer{ + funcs: b.funcs, + obj: b.funcs.CreateQuery(), + } +} + +func (b *Backend) IsTimeContinuous() bool { + return b.funcs.GetInteger(gl.GPU_DISJOINT_EXT) == gl.FALSE +} + +func (t *texture) ensureFBO() gl.Framebuffer { + if t.hasFBO { + return t.fbo + } + b := t.backend + oldFBO := b.glstate.drawFBO + defer func() { + b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, oldFBO) + }() + glErr(b.funcs) + fb := b.funcs.CreateFramebuffer() + b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, fb) + if err := glErr(b.funcs); err != nil { + b.funcs.DeleteFramebuffer(fb) + panic(err) + } + b.funcs.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, t.obj, 0) + if st := b.funcs.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE { + b.funcs.DeleteFramebuffer(fb) + panic(fmt.Errorf("incomplete framebuffer, status = 0x%x, err = %d", st, b.funcs.GetError())) + } + t.fbo = fb + t.hasFBO = true + return fb +} + +func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, minFilter, magFilter driver.TextureFilter, binding driver.BufferBinding) (driver.Texture, error) { + glErr(b.funcs) + tex := &texture{backend: b, obj: b.funcs.CreateTexture(), width: width, height: height, bindings: binding} + switch format { + case driver.TextureFormatFloat: + tex.triple = b.floatTriple + case driver.TextureFormatSRGBA: + tex.triple = b.srgbaTriple + case driver.TextureFormatRGBA8: + tex.triple = textureTriple{gl.RGBA8, gl.RGBA, gl.UNSIGNED_BYTE} + default: + return nil, errors.New("unsupported texture format") + } + b.BindTexture(0, tex) + b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, toTexFilter(magFilter)) + b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, toTexFilter(minFilter)) + b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) + b.funcs.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) + if b.gles && b.glver[0] >= 3 { + // Immutable textures are required for BindImageTexture, and can't hurt otherwise. + b.funcs.TexStorage2D(gl.TEXTURE_2D, 1, tex.triple.internalFormat, width, height) + } else { + b.funcs.TexImage2D(gl.TEXTURE_2D, 0, tex.triple.internalFormat, width, height, tex.triple.format, tex.triple.typ) + } + if err := glErr(b.funcs); err != nil { + tex.Release() + return nil, err + } + return tex, nil +} + +func (b *Backend) NewBuffer(typ driver.BufferBinding, size int) (driver.Buffer, error) { + glErr(b.funcs) + buf := &buffer{backend: b, typ: typ, size: size} + if typ&driver.BufferBindingUniforms != 0 { + if typ != driver.BufferBindingUniforms { + return nil, errors.New("uniforms buffers cannot be bound as anything else") + } + buf.data = make([]byte, size) + } + if typ&^driver.BufferBindingUniforms != 0 { + buf.hasBuffer = true + buf.obj = b.funcs.CreateBuffer() + if err := glErr(b.funcs); err != nil { + buf.Release() + return nil, err + } + firstBinding := firstBufferType(typ) + b.glstate.bindBuffer(b.funcs, firstBinding, buf.obj) + b.funcs.BufferData(firstBinding, size, gl.DYNAMIC_DRAW, nil) + } + return buf, nil +} + +func (b *Backend) NewImmutableBuffer(typ driver.BufferBinding, data []byte) (driver.Buffer, error) { + glErr(b.funcs) + obj := b.funcs.CreateBuffer() + buf := &buffer{backend: b, obj: obj, typ: typ, size: len(data), hasBuffer: true} + firstBinding := firstBufferType(typ) + b.glstate.bindBuffer(b.funcs, firstBinding, buf.obj) + b.funcs.BufferData(firstBinding, len(data), gl.STATIC_DRAW, data) + buf.immutable = true + if err := glErr(b.funcs); err != nil { + buf.Release() + return nil, err + } + return buf, nil +} + +func glErr(f *gl.Functions) error { + if st := f.GetError(); st != gl.NO_ERROR { + return fmt.Errorf("glGetError: %#x", st) + } + return nil +} + +func (b *Backend) Release() { + if b.sRGBFBO != nil { + b.sRGBFBO.Release() + } + if b.vertArray.Valid() { + b.glstate.deleteVertexArray(b.funcs, b.vertArray) + } + *b = Backend{} +} + +func (b *Backend) DispatchCompute(x, y, z int) { + for binding, buf := range b.storage { + if buf != nil { + b.glstate.bindBufferBase(b.funcs, gl.SHADER_STORAGE_BUFFER, binding, buf.obj) + } + } + b.funcs.DispatchCompute(x, y, z) + b.funcs.MemoryBarrier(gl.ALL_BARRIER_BITS) +} + +func (b *Backend) BindImageTexture(unit int, tex driver.Texture) { + t := tex.(*texture) + var acc gl.Enum + switch t.bindings & (driver.BufferBindingShaderStorageRead | driver.BufferBindingShaderStorageWrite) { + case driver.BufferBindingShaderStorageRead: + acc = gl.READ_ONLY + case driver.BufferBindingShaderStorageWrite: + acc = gl.WRITE_ONLY + case driver.BufferBindingShaderStorageRead | driver.BufferBindingShaderStorageWrite: + acc = gl.READ_WRITE + default: + panic("unsupported access bits") + } + b.funcs.BindImageTexture(unit, t.obj, 0, false, 0, acc, t.triple.internalFormat) +} + +func (b *Backend) BlendFunc(sfactor, dfactor driver.BlendFactor) { + src, dst := toGLBlendFactor(sfactor), toGLBlendFactor(dfactor) + b.glstate.setBlendFuncSeparate(b.funcs, src, dst, src, dst) +} + +func toGLBlendFactor(f driver.BlendFactor) gl.Enum { + switch f { + case driver.BlendFactorOne: + return gl.ONE + case driver.BlendFactorOneMinusSrcAlpha: + return gl.ONE_MINUS_SRC_ALPHA + case driver.BlendFactorZero: + return gl.ZERO + case driver.BlendFactorDstColor: + return gl.DST_COLOR + default: + panic("unsupported blend factor") + } +} + +func (b *Backend) SetBlend(enable bool) { + b.glstate.set(b.funcs, gl.BLEND, enable) +} + +func (b *Backend) DrawElements(off, count int) { + b.prepareDraw() + // off is in 16-bit indices, but DrawElements take a byte offset. + byteOff := off * 2 + b.funcs.DrawElements(toGLDrawMode(b.state.pipeline.topology), count, gl.UNSIGNED_SHORT, byteOff) +} + +func (b *Backend) DrawArrays(off, count int) { + b.prepareDraw() + b.funcs.DrawArrays(toGLDrawMode(b.state.pipeline.topology), off, count) +} + +func (b *Backend) prepareDraw() { + p := b.state.pipeline + if p == nil { + return + } + b.setupVertexArrays() +} + +func toGLDrawMode(mode driver.Topology) gl.Enum { + switch mode { + case driver.TopologyTriangleStrip: + return gl.TRIANGLE_STRIP + case driver.TopologyTriangles: + return gl.TRIANGLES + default: + panic("unsupported draw mode") + } +} + +func (b *Backend) Viewport(x, y, width, height int) { + b.glstate.setViewport(b.funcs, x, y, width, height) +} + +func (b *Backend) clearOutput(colR, colG, colB, colA float32) { + b.glstate.setClearColor(b.funcs, colR, colG, colB, colA) + b.funcs.Clear(gl.COLOR_BUFFER_BIT) +} + +func (b *Backend) NewComputeProgram(src shader.Sources) (driver.Program, error) { + // We don't support ES 3.1 compute, see brokenGLES31 above. + const GLES31Source = "" + p, err := gl.CreateComputeProgram(b.funcs, GLES31Source) + if err != nil { + return nil, fmt.Errorf("%s: %v", src.Name, err) + } + return &program{ + backend: b, + obj: p, + }, nil +} + +func (b *Backend) NewVertexShader(src shader.Sources) (driver.VertexShader, error) { + glslSrc := b.glslFor(src) + sh, err := gl.CreateShader(b.funcs, gl.VERTEX_SHADER, glslSrc) + return &glshader{backend: b, obj: sh, src: src}, err +} + +func (b *Backend) NewFragmentShader(src shader.Sources) (driver.FragmentShader, error) { + glslSrc := b.glslFor(src) + sh, err := gl.CreateShader(b.funcs, gl.FRAGMENT_SHADER, glslSrc) + return &glshader{backend: b, obj: sh, src: src}, err +} + +func (b *Backend) glslFor(src shader.Sources) string { + if b.gles { + return src.GLSL100ES + } else { + return src.GLSL150 + } +} + +func (b *Backend) NewPipeline(desc driver.PipelineDesc) (driver.Pipeline, error) { + p, err := b.newProgram(desc) + if err != nil { + return nil, err + } + layout := desc.VertexLayout + vsrc := desc.VertexShader.(*glshader).src + if len(vsrc.Inputs) != len(layout.Inputs) { + return nil, fmt.Errorf("opengl: got %d inputs, expected %d", len(layout.Inputs), len(vsrc.Inputs)) + } + for i, inp := range vsrc.Inputs { + if exp, got := inp.Size, layout.Inputs[i].Size; exp != got { + return nil, fmt.Errorf("opengl: data size mismatch for %q: got %d expected %d", inp.Name, got, exp) + } + } + return &pipeline{ + prog: p, + inputs: vsrc.Inputs, + layout: layout, + blend: desc.BlendDesc, + topology: desc.Topology, + }, nil +} + +func (b *Backend) newProgram(desc driver.PipelineDesc) (*program, error) { + p := b.funcs.CreateProgram() + if !p.Valid() { + return nil, errors.New("opengl: glCreateProgram failed") + } + vsh, fsh := desc.VertexShader.(*glshader), desc.FragmentShader.(*glshader) + b.funcs.AttachShader(p, vsh.obj) + b.funcs.AttachShader(p, fsh.obj) + for _, inp := range vsh.src.Inputs { + b.funcs.BindAttribLocation(p, gl.Attrib(inp.Location), inp.Name) + } + b.funcs.LinkProgram(p) + if b.funcs.GetProgrami(p, gl.LINK_STATUS) == 0 { + log := b.funcs.GetProgramInfoLog(p) + b.funcs.DeleteProgram(p) + return nil, fmt.Errorf("opengl: program link failed: %s", strings.TrimSpace(log)) + } + prog := &program{ + backend: b, + obj: p, + } + b.glstate.useProgram(b.funcs, p) + // Bind texture uniforms. + for _, tex := range vsh.src.Textures { + u := b.funcs.GetUniformLocation(p, tex.Name) + if u.Valid() { + b.funcs.Uniform1i(u, tex.Binding) + } + } + for _, tex := range fsh.src.Textures { + u := b.funcs.GetUniformLocation(p, tex.Name) + if u.Valid() { + b.funcs.Uniform1i(u, tex.Binding) + } + } + prog.vertUniforms.setup(b.funcs, p, vsh.src.Uniforms.Size, vsh.src.Uniforms.Locations) + prog.fragUniforms.setup(b.funcs, p, fsh.src.Uniforms.Size, fsh.src.Uniforms.Locations) + return prog, nil +} + +func (b *Backend) BindStorageBuffer(binding int, buf driver.Buffer) { + bf := buf.(*buffer) + if bf.typ&(driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite) == 0 { + panic("not a shader storage buffer") + } + b.storage[binding] = bf +} + +func (b *Backend) BindUniforms(buf driver.Buffer) { + bf := buf.(*buffer) + if bf.typ&driver.BufferBindingUniforms == 0 { + panic("not a uniform buffer") + } + b.state.pipeline.prog.vertUniforms.update(b.funcs, bf) + b.state.pipeline.prog.fragUniforms.update(b.funcs, bf) +} + +func (b *Backend) BindProgram(prog driver.Program) { + p := prog.(*program) + b.glstate.useProgram(b.funcs, p.obj) +} + +func (s *glshader) Release() { + s.backend.funcs.DeleteShader(s.obj) +} + +func (p *program) Release() { + p.backend.glstate.deleteProgram(p.backend.funcs, p.obj) +} + +func (u *uniforms) setup(funcs *gl.Functions, p gl.Program, uniformSize int, uniforms []shader.UniformLocation) { + u.locs = make([]uniformLocation, len(uniforms)) + for i, uniform := range uniforms { + loc := funcs.GetUniformLocation(p, uniform.Name) + u.locs[i] = uniformLocation{uniform: loc, offset: uniform.Offset, typ: uniform.Type, size: uniform.Size} + } + u.size = uniformSize +} + +func (p *uniforms) update(funcs *gl.Functions, buf *buffer) { + if buf.size < p.size { + panic(fmt.Errorf("uniform buffer too small, got %d need %d", buf.size, p.size)) + } + data := buf.data + for _, u := range p.locs { + if !u.uniform.Valid() { + continue + } + data := data[u.offset:] + switch { + case u.typ == shader.DataTypeFloat && u.size == 1: + data := data[:4] + v := *(*[1]float32)(unsafe.Pointer(&data[0])) + funcs.Uniform1f(u.uniform, v[0]) + case u.typ == shader.DataTypeFloat && u.size == 2: + data := data[:8] + v := *(*[2]float32)(unsafe.Pointer(&data[0])) + funcs.Uniform2f(u.uniform, v[0], v[1]) + case u.typ == shader.DataTypeFloat && u.size == 3: + data := data[:12] + v := *(*[3]float32)(unsafe.Pointer(&data[0])) + funcs.Uniform3f(u.uniform, v[0], v[1], v[2]) + case u.typ == shader.DataTypeFloat && u.size == 4: + data := data[:16] + v := *(*[4]float32)(unsafe.Pointer(&data[0])) + funcs.Uniform4f(u.uniform, v[0], v[1], v[2], v[3]) + default: + panic("unsupported uniform data type or size") + } + } +} + +func (b *buffer) Upload(data []byte) { + if b.immutable { + panic("immutable buffer") + } + if len(data) > b.size { + panic("buffer size overflow") + } + copy(b.data, data) + if b.hasBuffer { + firstBinding := firstBufferType(b.typ) + b.backend.glstate.bindBuffer(b.backend.funcs, firstBinding, b.obj) + if len(data) == b.size { + // the iOS GL implementation doesn't recognize when BufferSubData + // clears the entire buffer. Tell it and avoid GPU stalls. + // See also https://github.com/godotengine/godot/issues/23956. + b.backend.funcs.BufferData(firstBinding, b.size, gl.DYNAMIC_DRAW, data) + } else { + b.backend.funcs.BufferSubData(firstBinding, 0, data) + } + } +} + +func (b *buffer) Download(data []byte) error { + if len(data) > b.size { + panic("buffer size overflow") + } + if !b.hasBuffer { + copy(data, b.data) + return nil + } + firstBinding := firstBufferType(b.typ) + b.backend.glstate.bindBuffer(b.backend.funcs, firstBinding, b.obj) + bufferMap := b.backend.funcs.MapBufferRange(firstBinding, 0, len(data), gl.MAP_READ_BIT) + if bufferMap == nil { + return fmt.Errorf("MapBufferRange: error %#x", b.backend.funcs.GetError()) + } + copy(data, bufferMap) + if !b.backend.funcs.UnmapBuffer(firstBinding) { + return driver.ErrContentLost + } + return nil +} + +func (b *buffer) Release() { + if b.hasBuffer { + b.backend.glstate.deleteBuffer(b.backend.funcs, b.obj) + b.hasBuffer = false + } +} + +func (b *Backend) BindVertexBuffer(buf driver.Buffer, offset int) { + gbuf := buf.(*buffer) + if gbuf.typ&driver.BufferBindingVertices == 0 { + panic("not a vertex buffer") + } + b.state.buffer = bufferBinding{obj: gbuf.obj, offset: offset} +} + +func (b *Backend) setupVertexArrays() { + p := b.state.pipeline + inputs := p.inputs + if len(inputs) == 0 { + return + } + layout := p.layout + const max = len(b.glstate.vertAttribs) + var enabled [max]bool + buf := b.state.buffer + for i, inp := range inputs { + l := layout.Inputs[i] + var gltyp gl.Enum + switch l.Type { + case shader.DataTypeFloat: + gltyp = gl.FLOAT + case shader.DataTypeShort: + gltyp = gl.SHORT + default: + panic("unsupported data type") + } + enabled[inp.Location] = true + b.glstate.vertexAttribPointer(b.funcs, buf.obj, inp.Location, l.Size, gltyp, false, p.layout.Stride, buf.offset+l.Offset) + } + for i := 0; i < max; i++ { + b.glstate.setVertexAttribArray(b.funcs, i, enabled[i]) + } +} + +func (b *Backend) BindIndexBuffer(buf driver.Buffer) { + gbuf := buf.(*buffer) + if gbuf.typ&driver.BufferBindingIndices == 0 { + panic("not an index buffer") + } + b.glstate.bindBuffer(b.funcs, gl.ELEMENT_ARRAY_BUFFER, gbuf.obj) +} + +func (b *Backend) CopyTexture(dst driver.Texture, dstOrigin image.Point, src driver.Texture, srcRect image.Rectangle) { + const unit = 0 + oldTex := b.glstate.texUnits.binds[unit] + defer func() { + b.glstate.bindTexture(b.funcs, unit, oldTex) + }() + b.glstate.bindTexture(b.funcs, unit, dst.(*texture).obj) + b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, src.(*texture).ensureFBO()) + sz := srcRect.Size() + b.funcs.CopyTexSubImage2D(gl.TEXTURE_2D, 0, dstOrigin.X, dstOrigin.Y, srcRect.Min.X, srcRect.Min.Y, sz.X, sz.Y) +} + +func (t *texture) ReadPixels(src image.Rectangle, pixels []byte, stride int) error { + glErr(t.backend.funcs) + t.backend.glstate.bindFramebuffer(t.backend.funcs, gl.FRAMEBUFFER, t.ensureFBO()) + if len(pixels) < src.Dx()*src.Dy()*4 { + return errors.New("unexpected RGBA size") + } + w, h := src.Dx(), src.Dy() + // WebGL 1 doesn't support PACK_ROW_LENGTH != 0. Avoid it if possible. + rowLen := 0 + if n := stride / 4; n != w { + rowLen = n + } + t.backend.glstate.pixelStorei(t.backend.funcs, gl.PACK_ROW_LENGTH, rowLen) + t.backend.funcs.ReadPixels(src.Min.X, src.Min.Y, w, h, gl.RGBA, gl.UNSIGNED_BYTE, pixels) + return glErr(t.backend.funcs) +} + +func (b *Backend) BindPipeline(pl driver.Pipeline) { + p := pl.(*pipeline) + b.state.pipeline = p + b.glstate.useProgram(b.funcs, p.prog.obj) + b.SetBlend(p.blend.Enable) + b.BlendFunc(p.blend.SrcFactor, p.blend.DstFactor) +} + +func (b *Backend) BeginCompute() { + b.funcs.MemoryBarrier(gl.ALL_BARRIER_BITS) +} + +func (b *Backend) EndCompute() { +} + +func (b *Backend) BeginRenderPass(tex driver.Texture, desc driver.LoadDesc) { + fbo := tex.(*texture).ensureFBO() + b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, fbo) + switch desc.Action { + case driver.LoadActionClear: + c := desc.ClearColor + b.clearOutput(c.R, c.G, c.B, c.A) + case driver.LoadActionInvalidate: + b.funcs.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0) + } +} + +func (b *Backend) EndRenderPass() { +} + +func (f *texture) ImplementsRenderTarget() {} + +func (p *pipeline) Release() { + p.prog.Release() + *p = pipeline{} +} + +func toTexFilter(f driver.TextureFilter) int { + switch f { + case driver.FilterNearest: + return gl.NEAREST + case driver.FilterLinear: + return gl.LINEAR + default: + panic("unsupported texture filter") + } +} + +func (b *Backend) PrepareTexture(tex driver.Texture) {} + +func (b *Backend) BindTexture(unit int, t driver.Texture) { + b.glstate.bindTexture(b.funcs, unit, t.(*texture).obj) +} + +func (t *texture) Release() { + if t.foreign { + panic("texture not created by NewTexture") + } + if t.hasFBO { + t.backend.glstate.deleteFramebuffer(t.backend.funcs, t.fbo) + } + t.backend.glstate.deleteTexture(t.backend.funcs, t.obj) +} + +func (t *texture) Upload(offset, size image.Point, pixels []byte, stride int) { + if min := size.X * size.Y * 4; min > len(pixels) { + panic(fmt.Errorf("size %d larger than data %d", min, len(pixels))) + } + t.backend.BindTexture(0, t) + // WebGL 1 doesn't support UNPACK_ROW_LENGTH != 0. Avoid it if possible. + rowLen := 0 + if n := stride / 4; n != size.X { + rowLen = n + } + t.backend.glstate.pixelStorei(t.backend.funcs, gl.UNPACK_ROW_LENGTH, rowLen) + t.backend.funcs.TexSubImage2D(gl.TEXTURE_2D, 0, offset.X, offset.Y, size.X, size.Y, t.triple.format, t.triple.typ, pixels) +} + +func (t *timer) Begin() { + t.funcs.BeginQuery(gl.TIME_ELAPSED_EXT, t.obj) +} + +func (t *timer) End() { + t.funcs.EndQuery(gl.TIME_ELAPSED_EXT) +} + +func (t *timer) ready() bool { + return t.funcs.GetQueryObjectuiv(t.obj, gl.QUERY_RESULT_AVAILABLE) == gl.TRUE +} + +func (t *timer) Release() { + t.funcs.DeleteQuery(t.obj) +} + +func (t *timer) Duration() (time.Duration, bool) { + if !t.ready() { + return 0, false + } + nanos := t.funcs.GetQueryObjectuiv(t.obj, gl.QUERY_RESULT) + return time.Duration(nanos), true +} + +// floatTripleFor determines the best texture triple for floating point FBOs. +func floatTripleFor(f *gl.Functions, ver [2]int, exts []string) (textureTriple, error) { + var triples []textureTriple + if ver[0] >= 3 { + triples = append(triples, textureTriple{gl.R16F, gl.Enum(gl.RED), gl.Enum(gl.HALF_FLOAT)}) + } + // According to the OES_texture_half_float specification, EXT_color_buffer_half_float is needed to + // render to FBOs. However, the Safari WebGL1 implementation does support half-float FBOs but does not + // report EXT_color_buffer_half_float support. The triples are verified below, so it doesn't matter if we're + // wrong. + if hasExtension(exts, "GL_OES_texture_half_float") || hasExtension(exts, "GL_EXT_color_buffer_half_float") { + // Try single channel. + triples = append(triples, textureTriple{gl.LUMINANCE, gl.Enum(gl.LUMINANCE), gl.Enum(gl.HALF_FLOAT_OES)}) + // Fallback to 4 channels. + triples = append(triples, textureTriple{gl.RGBA, gl.Enum(gl.RGBA), gl.Enum(gl.HALF_FLOAT_OES)}) + } + if hasExtension(exts, "GL_OES_texture_float") || hasExtension(exts, "GL_EXT_color_buffer_float") { + triples = append(triples, textureTriple{gl.RGBA, gl.Enum(gl.RGBA), gl.Enum(gl.FLOAT)}) + } + tex := f.CreateTexture() + defer f.DeleteTexture(tex) + defTex := gl.Texture(f.GetBinding(gl.TEXTURE_BINDING_2D)) + defer f.BindTexture(gl.TEXTURE_2D, defTex) + f.BindTexture(gl.TEXTURE_2D, tex) + f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) + f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) + f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) + f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) + fbo := f.CreateFramebuffer() + defer f.DeleteFramebuffer(fbo) + defFBO := gl.Framebuffer(f.GetBinding(gl.FRAMEBUFFER_BINDING)) + f.BindFramebuffer(gl.FRAMEBUFFER, fbo) + defer f.BindFramebuffer(gl.FRAMEBUFFER, defFBO) + var attempts []string + for _, tt := range triples { + const size = 256 + f.TexImage2D(gl.TEXTURE_2D, 0, tt.internalFormat, size, size, tt.format, tt.typ) + f.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0) + st := f.CheckFramebufferStatus(gl.FRAMEBUFFER) + if st == gl.FRAMEBUFFER_COMPLETE { + return tt, nil + } + attempts = append(attempts, fmt.Sprintf("(0x%x, 0x%x, 0x%x): 0x%x", tt.internalFormat, tt.format, tt.typ, st)) + } + return textureTriple{}, fmt.Errorf("floating point fbos not supported (attempted %s)", attempts) +} + +func srgbaTripleFor(ver [2]int, exts []string) (textureTriple, error) { + switch { + case ver[0] >= 3: + return textureTriple{gl.SRGB8_ALPHA8, gl.Enum(gl.RGBA), gl.Enum(gl.UNSIGNED_BYTE)}, nil + case hasExtension(exts, "GL_EXT_sRGB"): + return textureTriple{gl.SRGB_ALPHA_EXT, gl.Enum(gl.SRGB_ALPHA_EXT), gl.Enum(gl.UNSIGNED_BYTE)}, nil + default: + return textureTriple{}, errors.New("no sRGB texture formats found") + } +} + +func alphaTripleFor(ver [2]int) textureTriple { + intf, f := gl.Enum(gl.R8), gl.Enum(gl.RED) + if ver[0] < 3 { + // R8, RED not supported on OpenGL ES 2.0. + intf, f = gl.LUMINANCE, gl.Enum(gl.LUMINANCE) + } + return textureTriple{intf, f, gl.UNSIGNED_BYTE} +} + +func hasExtension(exts []string, ext string) bool { + for _, e := range exts { + if ext == e { + return true + } + } + return false +} + +func firstBufferType(typ driver.BufferBinding) gl.Enum { + switch { + case typ&driver.BufferBindingIndices != 0: + return gl.ELEMENT_ARRAY_BUFFER + case typ&driver.BufferBindingVertices != 0: + return gl.ARRAY_BUFFER + case typ&driver.BufferBindingUniforms != 0: + return gl.UNIFORM_BUFFER + case typ&(driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite) != 0: + return gl.SHADER_STORAGE_BUFFER + default: + panic("unsupported buffer type") + } +} diff --git a/vendor/gioui.org/gpu/internal/opengl/srgb.go b/vendor/gioui.org/gpu/internal/opengl/srgb.go new file mode 100644 index 0000000..4871d94 --- /dev/null +++ b/vendor/gioui.org/gpu/internal/opengl/srgb.go @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package opengl + +import ( + "errors" + "fmt" + "image" + "runtime" + "strings" + + "gioui.org/internal/byteslice" + "gioui.org/internal/gl" +) + +// SRGBFBO implements an intermediate sRGB FBO +// for gamma-correct rendering on platforms without +// sRGB enabled native framebuffers. +type SRGBFBO struct { + c *gl.Functions + state *glState + viewport image.Point + fbo gl.Framebuffer + tex gl.Texture + blitted bool + quad gl.Buffer + prog gl.Program + format textureTriple +} + +func NewSRGBFBO(f *gl.Functions, state *glState) (*SRGBFBO, error) { + glVer := f.GetString(gl.VERSION) + ver, _, err := gl.ParseGLVersion(glVer) + if err != nil { + return nil, err + } + exts := strings.Split(f.GetString(gl.EXTENSIONS), " ") + srgbTriple, err := srgbaTripleFor(ver, exts) + if err != nil { + // Fall back to the linear RGB colorspace, at the cost of color precision loss. + srgbTriple = textureTriple{gl.RGBA, gl.Enum(gl.RGBA), gl.Enum(gl.UNSIGNED_BYTE)} + } + s := &SRGBFBO{ + c: f, + state: state, + format: srgbTriple, + fbo: f.CreateFramebuffer(), + tex: f.CreateTexture(), + } + state.bindTexture(f, 0, s.tex) + f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) + f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) + f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) + f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) + return s, nil +} + +func (s *SRGBFBO) Blit() { + if !s.blitted { + prog, err := gl.CreateProgram(s.c, blitVSrc, blitFSrc, []string{"pos", "uv"}) + if err != nil { + panic(err) + } + s.prog = prog + s.state.useProgram(s.c, prog) + s.c.Uniform1i(s.c.GetUniformLocation(prog, "tex"), 0) + s.quad = s.c.CreateBuffer() + s.state.bindBuffer(s.c, gl.ARRAY_BUFFER, s.quad) + coords := byteslice.Slice([]float32{ + -1, +1, 0, 1, + +1, +1, 1, 1, + -1, -1, 0, 0, + +1, -1, 1, 0, + }) + s.c.BufferData(gl.ARRAY_BUFFER, len(coords), gl.STATIC_DRAW, coords) + s.blitted = true + } + s.state.useProgram(s.c, s.prog) + s.state.bindTexture(s.c, 0, s.tex) + s.state.vertexAttribPointer(s.c, s.quad, 0 /* pos */, 2, gl.FLOAT, false, 4*4, 0) + s.state.vertexAttribPointer(s.c, s.quad, 1 /* uv */, 2, gl.FLOAT, false, 4*4, 4*2) + s.state.setVertexAttribArray(s.c, 0, true) + s.state.setVertexAttribArray(s.c, 1, true) + s.c.DrawArrays(gl.TRIANGLE_STRIP, 0, 4) + s.state.bindFramebuffer(s.c, gl.FRAMEBUFFER, s.fbo) + s.c.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0) +} + +func (s *SRGBFBO) Framebuffer() gl.Framebuffer { + return s.fbo +} + +func (s *SRGBFBO) Refresh(viewport image.Point) error { + if viewport.X == 0 || viewport.Y == 0 { + return errors.New("srgb: zero-sized framebuffer") + } + if s.viewport == viewport { + return nil + } + s.viewport = viewport + s.state.bindTexture(s.c, 0, s.tex) + s.c.TexImage2D(gl.TEXTURE_2D, 0, s.format.internalFormat, viewport.X, viewport.Y, s.format.format, s.format.typ) + s.state.bindFramebuffer(s.c, gl.FRAMEBUFFER, s.fbo) + s.c.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, s.tex, 0) + if st := s.c.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE { + return fmt.Errorf("sRGB framebuffer incomplete (%dx%d), status: %#x error: %x", viewport.X, viewport.Y, st, s.c.GetError()) + } + + if runtime.GOOS == "js" { + // With macOS Safari, rendering to and then reading from a SRGB8_ALPHA8 + // texture result in twice gamma corrected colors. Using a plain RGBA + // texture seems to work. + s.state.setClearColor(s.c, .5, .5, .5, 1.0) + s.c.Clear(gl.COLOR_BUFFER_BIT) + var pixel [4]byte + s.c.ReadPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel[:]) + if pixel[0] == 128 { // Correct sRGB color value is ~188 + s.c.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, viewport.X, viewport.Y, gl.RGBA, gl.UNSIGNED_BYTE) + if st := s.c.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE { + return fmt.Errorf("fallback RGBA framebuffer incomplete (%dx%d), status: %#x error: %x", viewport.X, viewport.Y, st, s.c.GetError()) + } + } + } + + return nil +} + +func (s *SRGBFBO) Release() { + s.state.deleteFramebuffer(s.c, s.fbo) + s.state.deleteTexture(s.c, s.tex) + if s.blitted { + s.state.deleteBuffer(s.c, s.quad) + s.state.deleteProgram(s.c, s.prog) + } + s.c = nil +} + +const ( + blitVSrc = ` +#version 100 + +precision highp float; + +attribute vec2 pos; +attribute vec2 uv; + +varying vec2 vUV; + +void main() { + gl_Position = vec4(pos, 0, 1); + vUV = uv; +} +` + blitFSrc = ` +#version 100 + +precision mediump float; + +uniform sampler2D tex; +varying vec2 vUV; + +vec3 gamma(vec3 rgb) { + vec3 exp = vec3(1.055)*pow(rgb, vec3(0.41666)) - vec3(0.055); + vec3 lin = rgb * vec3(12.92); + bvec3 cut = lessThan(rgb, vec3(0.0031308)); + return vec3(cut.r ? lin.r : exp.r, cut.g ? lin.g : exp.g, cut.b ? lin.b : exp.b); +} + +void main() { + vec4 col = texture2D(tex, vUV); + vec3 rgb = col.rgb; + rgb = gamma(rgb); + gl_FragColor = vec4(rgb, col.a); +} +` +) diff --git a/vendor/gioui.org/gpu/internal/vulkan/vulkan.go b/vendor/gioui.org/gpu/internal/vulkan/vulkan.go new file mode 100644 index 0000000..7d3791e --- /dev/null +++ b/vendor/gioui.org/gpu/internal/vulkan/vulkan.go @@ -0,0 +1,1121 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build (linux || freebsd) && !novulkan +// +build linux freebsd +// +build !novulkan + +package vulkan + +import ( + "errors" + "fmt" + "image" + + "gioui.org/gpu/internal/driver" + "gioui.org/internal/vk" + "gioui.org/shader" +) + +type Backend struct { + physDev vk.PhysicalDevice + dev vk.Device + queue vk.Queue + cmdPool struct { + current vk.CommandBuffer + pool vk.CommandPool + used int + buffers []vk.CommandBuffer + } + outFormat vk.Format + staging struct { + buf *Buffer + mem []byte + size int + cap int + } + defers []func(d vk.Device) + frameSig vk.Semaphore + waitSems []vk.Semaphore + waitStages []vk.PipelineStageFlags + sigSems []vk.Semaphore + fence vk.Fence + + allPipes []*Pipeline + + pipe *Pipeline + + passes map[passKey]vk.RenderPass + + // bindings and offset are temporary storage for BindVertexBuffer. + bindings []vk.Buffer + offsets []vk.DeviceSize + + desc struct { + dirty bool + texBinds [texUnits]*Texture + bufBinds [storageUnits]*Buffer + } + + caps driver.Features +} + +type passKey struct { + fmt vk.Format + loadAct vk.AttachmentLoadOp + initLayout vk.ImageLayout + finalLayout vk.ImageLayout +} + +type Texture struct { + backend *Backend + img vk.Image + mem vk.DeviceMemory + view vk.ImageView + sampler vk.Sampler + fbo vk.Framebuffer + format vk.Format + layout vk.ImageLayout + passLayout vk.ImageLayout + width int + height int + acquire vk.Semaphore + foreign bool + + scope struct { + stage vk.PipelineStageFlags + access vk.AccessFlags + } +} + +type Shader struct { + dev vk.Device + module vk.ShaderModule + pushRange vk.PushConstantRange + src shader.Sources +} + +type Pipeline struct { + backend *Backend + pipe vk.Pipeline + pushRanges []vk.PushConstantRange + ninputs int + desc *descPool +} + +type descPool struct { + layout vk.PipelineLayout + descLayout vk.DescriptorSetLayout + pool vk.DescriptorPool + size int + cap int + texBinds []int + imgBinds []int + bufBinds []int +} + +type Buffer struct { + backend *Backend + buf vk.Buffer + store []byte + mem vk.DeviceMemory + usage vk.BufferUsageFlags + + scope struct { + stage vk.PipelineStageFlags + access vk.AccessFlags + } +} + +const ( + texUnits = 4 + storageUnits = 4 +) + +func init() { + driver.NewVulkanDevice = newVulkanDevice +} + +func newVulkanDevice(api driver.Vulkan) (driver.Device, error) { + b := &Backend{ + physDev: vk.PhysicalDevice(api.PhysDevice), + dev: vk.Device(api.Device), + outFormat: vk.Format(api.Format), + caps: driver.FeatureCompute, + passes: make(map[passKey]vk.RenderPass), + } + b.queue = vk.GetDeviceQueue(b.dev, api.QueueFamily, api.QueueIndex) + cmdPool, err := vk.CreateCommandPool(b.dev, api.QueueFamily) + if err != nil { + return nil, err + } + b.cmdPool.pool = cmdPool + props := vk.GetPhysicalDeviceFormatProperties(b.physDev, vk.FORMAT_R16_SFLOAT) + reqs := vk.FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | vk.FORMAT_FEATURE_SAMPLED_IMAGE_BIT + if props&reqs == reqs { + b.caps |= driver.FeatureFloatRenderTargets + } + reqs = vk.FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT | vk.FORMAT_FEATURE_SAMPLED_IMAGE_BIT + props = vk.GetPhysicalDeviceFormatProperties(b.physDev, vk.FORMAT_R8G8B8A8_SRGB) + if props&reqs == reqs { + b.caps |= driver.FeatureSRGB + } + fence, err := vk.CreateFence(b.dev) + if err != nil { + return nil, mapErr(err) + } + b.fence = fence + return b, nil +} + +func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Texture { + vk.QueueWaitIdle(b.queue) + b.staging.size = 0 + b.cmdPool.used = 0 + b.runDefers() + b.resetPipes() + + if target == nil { + return nil + } + switch t := target.(type) { + case driver.VulkanRenderTarget: + layout := vk.IMAGE_LAYOUT_UNDEFINED + if !clear { + layout = vk.IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + } + b.frameSig = vk.Semaphore(t.SignalSem) + tex := &Texture{ + img: vk.Image(t.Image), + fbo: vk.Framebuffer(t.Framebuffer), + width: viewport.X, + height: viewport.Y, + layout: layout, + passLayout: vk.IMAGE_LAYOUT_PRESENT_SRC_KHR, + format: b.outFormat, + acquire: vk.Semaphore(t.WaitSem), + foreign: true, + } + return tex + case *Texture: + return t + default: + panic(fmt.Sprintf("vulkan: unsupported render target type: %T", t)) + } +} + +func (b *Backend) deferFunc(f func(d vk.Device)) { + b.defers = append(b.defers, f) +} + +func (b *Backend) runDefers() { + for _, f := range b.defers { + f(b.dev) + } + b.defers = b.defers[:0] +} + +func (b *Backend) resetPipes() { + for i := len(b.allPipes) - 1; i >= 0; i-- { + p := b.allPipes[i] + if p.pipe == 0 { + // Released pipeline. + b.allPipes = append(b.allPipes[:i], b.allPipes[:i+1]...) + continue + } + if p.desc.size > 0 { + vk.ResetDescriptorPool(b.dev, p.desc.pool) + p.desc.size = 0 + } + } +} + +func (b *Backend) EndFrame() { + if b.frameSig != 0 { + b.sigSems = append(b.sigSems, b.frameSig) + b.frameSig = 0 + } + b.submitCmdBuf(false) +} + +func (b *Backend) Caps() driver.Caps { + return driver.Caps{ + MaxTextureSize: 4096, + Features: b.caps, + } +} + +func (b *Backend) NewTimer() driver.Timer { + panic("timers not supported") +} + +func (b *Backend) IsTimeContinuous() bool { + panic("timers not supported") +} + +func (b *Backend) Release() { + vk.DeviceWaitIdle(b.dev) + if buf := b.staging.buf; buf != nil { + vk.UnmapMemory(b.dev, b.staging.buf.mem) + buf.Release() + } + b.runDefers() + for _, rp := range b.passes { + vk.DestroyRenderPass(b.dev, rp) + } + vk.DestroyFence(b.dev, b.fence) + vk.FreeCommandBuffers(b.dev, b.cmdPool.pool, b.cmdPool.buffers...) + vk.DestroyCommandPool(b.dev, b.cmdPool.pool) + *b = Backend{} +} + +func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, minFilter, magFilter driver.TextureFilter, bindings driver.BufferBinding) (driver.Texture, error) { + vkfmt := formatFor(format) + usage := vk.IMAGE_USAGE_TRANSFER_DST_BIT | vk.IMAGE_USAGE_TRANSFER_SRC_BIT + passLayout := vk.IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + if bindings&driver.BufferBindingTexture != 0 { + usage |= vk.IMAGE_USAGE_SAMPLED_BIT + passLayout = vk.IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL + } + if bindings&driver.BufferBindingFramebuffer != 0 { + usage |= vk.IMAGE_USAGE_COLOR_ATTACHMENT_BIT + } + if bindings&(driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite) != 0 { + usage |= vk.IMAGE_USAGE_STORAGE_BIT + } + filterFor := func(f driver.TextureFilter) vk.Filter { + switch minFilter { + case driver.FilterLinear: + return vk.FILTER_LINEAR + case driver.FilterNearest: + return vk.FILTER_NEAREST + } + panic("unknown filter") + } + sampler, err := vk.CreateSampler(b.dev, filterFor(minFilter), filterFor(magFilter)) + if err != nil { + return nil, mapErr(err) + } + img, mem, err := vk.CreateImage(b.physDev, b.dev, vkfmt, width, height, usage) + if err != nil { + vk.DestroySampler(b.dev, sampler) + return nil, mapErr(err) + } + view, err := vk.CreateImageView(b.dev, img, vkfmt) + if err != nil { + vk.DestroySampler(b.dev, sampler) + vk.DestroyImage(b.dev, img) + vk.FreeMemory(b.dev, mem) + return nil, mapErr(err) + } + t := &Texture{backend: b, img: img, mem: mem, view: view, sampler: sampler, layout: vk.IMAGE_LAYOUT_UNDEFINED, passLayout: passLayout, width: width, height: height, format: vkfmt} + if bindings&driver.BufferBindingFramebuffer != 0 { + pass, err := vk.CreateRenderPass(b.dev, vkfmt, vk.ATTACHMENT_LOAD_OP_DONT_CARE, + vk.IMAGE_LAYOUT_UNDEFINED, vk.IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, nil) + if err != nil { + return nil, mapErr(err) + } + defer vk.DestroyRenderPass(b.dev, pass) + fbo, err := vk.CreateFramebuffer(b.dev, pass, view, width, height) + if err != nil { + return nil, mapErr(err) + } + t.fbo = fbo + } + return t, nil +} + +func (b *Backend) NewBuffer(bindings driver.BufferBinding, size int) (driver.Buffer, error) { + if bindings&driver.BufferBindingUniforms != 0 { + // Implement uniform buffers as inline push constants. + return &Buffer{store: make([]byte, size)}, nil + } + usage := vk.BUFFER_USAGE_TRANSFER_DST_BIT | vk.BUFFER_USAGE_TRANSFER_SRC_BIT + if bindings&driver.BufferBindingIndices != 0 { + usage |= vk.BUFFER_USAGE_INDEX_BUFFER_BIT + } + if bindings&(driver.BufferBindingShaderStorageRead|driver.BufferBindingShaderStorageWrite) != 0 { + usage |= vk.BUFFER_USAGE_STORAGE_BUFFER_BIT + } + if bindings&driver.BufferBindingVertices != 0 { + usage |= vk.BUFFER_USAGE_VERTEX_BUFFER_BIT + } + buf, err := b.newBuffer(size, usage, vk.MEMORY_PROPERTY_DEVICE_LOCAL_BIT) + return buf, mapErr(err) +} + +func (b *Backend) newBuffer(size int, usage vk.BufferUsageFlags, props vk.MemoryPropertyFlags) (*Buffer, error) { + buf, mem, err := vk.CreateBuffer(b.physDev, b.dev, size, usage, props) + return &Buffer{backend: b, buf: buf, mem: mem, usage: usage}, err +} + +func (b *Backend) NewImmutableBuffer(typ driver.BufferBinding, data []byte) (driver.Buffer, error) { + buf, err := b.NewBuffer(typ, len(data)) + if err != nil { + return nil, err + } + buf.Upload(data) + return buf, nil +} + +func (b *Backend) NewVertexShader(src shader.Sources) (driver.VertexShader, error) { + sh, err := b.newShader(src, vk.SHADER_STAGE_VERTEX_BIT) + return sh, mapErr(err) +} + +func (b *Backend) NewFragmentShader(src shader.Sources) (driver.FragmentShader, error) { + sh, err := b.newShader(src, vk.SHADER_STAGE_FRAGMENT_BIT) + return sh, mapErr(err) +} + +func (b *Backend) NewPipeline(desc driver.PipelineDesc) (driver.Pipeline, error) { + vs := desc.VertexShader.(*Shader) + fs := desc.FragmentShader.(*Shader) + var ranges []vk.PushConstantRange + if r := vs.pushRange; r != (vk.PushConstantRange{}) { + ranges = append(ranges, r) + } + if r := fs.pushRange; r != (vk.PushConstantRange{}) { + ranges = append(ranges, r) + } + descPool, err := createPipelineLayout(b.dev, fs.src, ranges) + if err != nil { + return nil, mapErr(err) + } + blend := desc.BlendDesc + factorFor := func(f driver.BlendFactor) vk.BlendFactor { + switch f { + case driver.BlendFactorZero: + return vk.BLEND_FACTOR_ZERO + case driver.BlendFactorOne: + return vk.BLEND_FACTOR_ONE + case driver.BlendFactorOneMinusSrcAlpha: + return vk.BLEND_FACTOR_ONE_MINUS_SRC_ALPHA + case driver.BlendFactorDstColor: + return vk.BLEND_FACTOR_DST_COLOR + default: + panic("unknown blend factor") + } + } + var top vk.PrimitiveTopology + switch desc.Topology { + case driver.TopologyTriangles: + top = vk.PRIMITIVE_TOPOLOGY_TRIANGLE_LIST + case driver.TopologyTriangleStrip: + top = vk.PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP + default: + panic("unknown topology") + } + var binds []vk.VertexInputBindingDescription + var attrs []vk.VertexInputAttributeDescription + inputs := desc.VertexLayout.Inputs + for i, inp := range inputs { + binds = append(binds, vk.VertexInputBindingDescription{ + Binding: i, + Stride: desc.VertexLayout.Stride, + }) + attrs = append(attrs, vk.VertexInputAttributeDescription{ + Binding: i, + Location: vs.src.Inputs[i].Location, + Format: vertFormatFor(vs.src.Inputs[i]), + Offset: inp.Offset, + }) + } + fmt := b.outFormat + if f := desc.PixelFormat; f != driver.TextureFormatOutput { + fmt = formatFor(f) + } + pass, err := vk.CreateRenderPass(b.dev, fmt, vk.ATTACHMENT_LOAD_OP_DONT_CARE, + vk.IMAGE_LAYOUT_UNDEFINED, vk.IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, nil) + if err != nil { + return nil, mapErr(err) + } + defer vk.DestroyRenderPass(b.dev, pass) + pipe, err := vk.CreateGraphicsPipeline(b.dev, pass, vs.module, fs.module, blend.Enable, factorFor(blend.SrcFactor), factorFor(blend.DstFactor), top, binds, attrs, descPool.layout) + if err != nil { + descPool.release(b.dev) + return nil, mapErr(err) + } + p := &Pipeline{backend: b, pipe: pipe, desc: descPool, pushRanges: ranges, ninputs: len(inputs)} + b.allPipes = append(b.allPipes, p) + return p, nil +} + +func (b *Backend) NewComputeProgram(src shader.Sources) (driver.Program, error) { + sh, err := b.newShader(src, vk.SHADER_STAGE_COMPUTE_BIT) + if err != nil { + return nil, mapErr(err) + } + defer sh.Release() + descPool, err := createPipelineLayout(b.dev, src, nil) + if err != nil { + return nil, mapErr(err) + } + pipe, err := vk.CreateComputePipeline(b.dev, sh.module, descPool.layout) + if err != nil { + descPool.release(b.dev) + return nil, mapErr(err) + } + return &Pipeline{backend: b, pipe: pipe, desc: descPool}, nil +} + +func vertFormatFor(f shader.InputLocation) vk.Format { + t := f.Type + s := f.Size + switch { + case t == shader.DataTypeFloat && s == 1: + return vk.FORMAT_R32_SFLOAT + case t == shader.DataTypeFloat && s == 2: + return vk.FORMAT_R32G32_SFLOAT + case t == shader.DataTypeFloat && s == 3: + return vk.FORMAT_R32G32B32_SFLOAT + case t == shader.DataTypeFloat && s == 4: + return vk.FORMAT_R32G32B32A32_SFLOAT + default: + panic("unsupported data type") + } +} + +func createPipelineLayout(d vk.Device, src shader.Sources, ranges []vk.PushConstantRange) (*descPool, error) { + var ( + descLayouts []vk.DescriptorSetLayout + descLayout vk.DescriptorSetLayout + ) + texBinds := make([]int, len(src.Textures)) + imgBinds := make([]int, len(src.Images)) + bufBinds := make([]int, len(src.StorageBuffers)) + var descBinds []vk.DescriptorSetLayoutBinding + for i, t := range src.Textures { + descBinds = append(descBinds, vk.DescriptorSetLayoutBinding{ + Binding: t.Binding, + StageFlags: vk.SHADER_STAGE_FRAGMENT_BIT, + DescriptorType: vk.DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + }) + texBinds[i] = t.Binding + } + for i, img := range src.Images { + descBinds = append(descBinds, vk.DescriptorSetLayoutBinding{ + Binding: img.Binding, + StageFlags: vk.SHADER_STAGE_COMPUTE_BIT, + DescriptorType: vk.DESCRIPTOR_TYPE_STORAGE_IMAGE, + }) + imgBinds[i] = img.Binding + } + for i, buf := range src.StorageBuffers { + descBinds = append(descBinds, vk.DescriptorSetLayoutBinding{ + Binding: buf.Binding, + StageFlags: vk.SHADER_STAGE_COMPUTE_BIT, + DescriptorType: vk.DESCRIPTOR_TYPE_STORAGE_BUFFER, + }) + bufBinds[i] = buf.Binding + } + if len(descBinds) > 0 { + var err error + descLayout, err = vk.CreateDescriptorSetLayout(d, descBinds) + if err != nil { + return nil, err + } + descLayouts = append(descLayouts, descLayout) + } + layout, err := vk.CreatePipelineLayout(d, ranges, descLayouts) + if err != nil { + if descLayout != 0 { + vk.DestroyDescriptorSetLayout(d, descLayout) + } + return nil, err + } + descPool := &descPool{ + texBinds: texBinds, + bufBinds: bufBinds, + imgBinds: imgBinds, + layout: layout, + descLayout: descLayout, + } + return descPool, nil +} + +func (b *Backend) newShader(src shader.Sources, stage vk.ShaderStageFlags) (*Shader, error) { + mod, err := vk.CreateShaderModule(b.dev, src.SPIRV) + if err != nil { + return nil, err + } + + sh := &Shader{dev: b.dev, module: mod, src: src} + if locs := src.Uniforms.Locations; len(locs) > 0 { + pushOffset := 0x7fffffff + for _, l := range locs { + if l.Offset < pushOffset { + pushOffset = l.Offset + } + } + sh.pushRange = vk.BuildPushConstantRange(stage, pushOffset, src.Uniforms.Size) + } + return sh, nil +} + +func (b *Backend) CopyTexture(dstTex driver.Texture, dorig image.Point, srcFBO driver.Texture, srect image.Rectangle) { + dst := dstTex.(*Texture) + src := srcFBO.(*Texture) + cmdBuf := b.ensureCmdBuf() + op := vk.BuildImageCopy(srect.Min.X, srect.Min.Y, dorig.X, dorig.Y, srect.Dx(), srect.Dy()) + src.imageBarrier(cmdBuf, + vk.IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + vk.PIPELINE_STAGE_TRANSFER_BIT, + vk.ACCESS_TRANSFER_READ_BIT, + ) + dst.imageBarrier(cmdBuf, + vk.IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + vk.PIPELINE_STAGE_TRANSFER_BIT, + vk.ACCESS_TRANSFER_WRITE_BIT, + ) + vk.CmdCopyImage(cmdBuf, src.img, src.layout, dst.img, dst.layout, []vk.ImageCopy{op}) +} + +func (b *Backend) Viewport(x, y, width, height int) { + cmdBuf := b.currentCmdBuf() + vp := vk.BuildViewport(float32(x), float32(y), float32(width), float32(height)) + vk.CmdSetViewport(cmdBuf, 0, vp) +} + +func (b *Backend) DrawArrays(off, count int) { + cmdBuf := b.currentCmdBuf() + if b.desc.dirty { + b.pipe.desc.bindDescriptorSet(b, cmdBuf, vk.PIPELINE_BIND_POINT_GRAPHICS, b.desc.texBinds, b.desc.bufBinds) + b.desc.dirty = false + } + vk.CmdDraw(cmdBuf, count, 1, off, 0) +} + +func (b *Backend) DrawElements(off, count int) { + cmdBuf := b.currentCmdBuf() + if b.desc.dirty { + b.pipe.desc.bindDescriptorSet(b, cmdBuf, vk.PIPELINE_BIND_POINT_GRAPHICS, b.desc.texBinds, b.desc.bufBinds) + b.desc.dirty = false + } + vk.CmdDrawIndexed(cmdBuf, count, 1, off, 0, 0) +} + +func (b *Backend) BindImageTexture(unit int, tex driver.Texture) { + t := tex.(*Texture) + b.desc.texBinds[unit] = t + b.desc.dirty = true + t.imageBarrier(b.currentCmdBuf(), + vk.IMAGE_LAYOUT_GENERAL, + vk.PIPELINE_STAGE_COMPUTE_SHADER_BIT, + vk.ACCESS_SHADER_READ_BIT|vk.ACCESS_SHADER_WRITE_BIT, + ) +} + +func (b *Backend) DispatchCompute(x, y, z int) { + cmdBuf := b.currentCmdBuf() + if b.desc.dirty { + b.pipe.desc.bindDescriptorSet(b, cmdBuf, vk.PIPELINE_BIND_POINT_COMPUTE, b.desc.texBinds, b.desc.bufBinds) + b.desc.dirty = false + } + vk.CmdDispatch(cmdBuf, x, y, z) +} + +func (t *Texture) Upload(offset, size image.Point, pixels []byte, stride int) { + if stride == 0 { + stride = size.X * 4 + } + cmdBuf := t.backend.ensureCmdBuf() + dstStride := size.X * 4 + n := size.Y * dstStride + stage, mem, off := t.backend.stagingBuffer(n) + var srcOff, dstOff int + for y := 0; y < size.Y; y++ { + srcRow := pixels[srcOff : srcOff+dstStride] + dstRow := mem[dstOff : dstOff+dstStride] + copy(dstRow, srcRow) + dstOff += dstStride + srcOff += stride + } + op := vk.BuildBufferImageCopy(off, dstStride/4, offset.X, offset.Y, size.X, size.Y) + t.imageBarrier(cmdBuf, + vk.IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + vk.PIPELINE_STAGE_TRANSFER_BIT, + vk.ACCESS_TRANSFER_WRITE_BIT, + ) + vk.CmdCopyBufferToImage(cmdBuf, stage.buf, t.img, t.layout, op) +} + +func (t *Texture) Release() { + if t.foreign { + panic("external textures cannot be released") + } + freet := *t + t.backend.deferFunc(func(d vk.Device) { + if freet.fbo != 0 { + vk.DestroyFramebuffer(d, freet.fbo) + } + vk.DestroySampler(d, freet.sampler) + vk.DestroyImageView(d, freet.view) + vk.DestroyImage(d, freet.img) + vk.FreeMemory(d, freet.mem) + }) + *t = Texture{} +} + +func (p *Pipeline) Release() { + freep := *p + p.backend.deferFunc(func(d vk.Device) { + freep.desc.release(d) + vk.DestroyPipeline(d, freep.pipe) + }) + *p = Pipeline{} +} + +func (p *descPool) release(d vk.Device) { + if p := p.pool; p != 0 { + vk.DestroyDescriptorPool(d, p) + } + if l := p.descLayout; l != 0 { + vk.DestroyDescriptorSetLayout(d, l) + } + vk.DestroyPipelineLayout(d, p.layout) +} + +func (p *descPool) bindDescriptorSet(b *Backend, cmdBuf vk.CommandBuffer, bindPoint vk.PipelineBindPoint, texBinds [texUnits]*Texture, bufBinds [storageUnits]*Buffer) { + realloced := false + destroyPool := func() { + if pool := p.pool; pool != 0 { + b.deferFunc(func(d vk.Device) { + vk.DestroyDescriptorPool(d, pool) + }) + } + p.pool = 0 + p.cap = 0 + } + for { + if p.size == p.cap { + if realloced { + panic("vulkan: vkAllocateDescriptorSet failed on a newly allocated descriptor pool") + } + destroyPool() + realloced = true + newCap := p.cap * 2 + const initialPoolSize = 100 + if newCap < initialPoolSize { + newCap = initialPoolSize + } + var poolSizes []vk.DescriptorPoolSize + if n := len(p.texBinds); n > 0 { + poolSizes = append(poolSizes, vk.BuildDescriptorPoolSize(vk.DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, newCap*n)) + } + if n := len(p.imgBinds); n > 0 { + poolSizes = append(poolSizes, vk.BuildDescriptorPoolSize(vk.DESCRIPTOR_TYPE_STORAGE_IMAGE, newCap*n)) + } + if n := len(p.bufBinds); n > 0 { + poolSizes = append(poolSizes, vk.BuildDescriptorPoolSize(vk.DESCRIPTOR_TYPE_STORAGE_BUFFER, newCap*n)) + } + pool, err := vk.CreateDescriptorPool(b.dev, newCap, poolSizes) + if err != nil { + panic(fmt.Errorf("vulkan: failed to allocate descriptor pool with %d descriptors", newCap)) + } + p.pool = pool + p.cap = newCap + p.size = 0 + } + l := p.descLayout + if l == 0 { + panic("vulkan: descriptor set is dirty, but pipeline has empty layout") + } + descSet, err := vk.AllocateDescriptorSet(b.dev, p.pool, l) + if err != nil { + destroyPool() + continue + } + p.size++ + for _, bind := range p.texBinds { + tex := texBinds[bind] + write := vk.BuildWriteDescriptorSetImage(descSet, bind, vk.DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, tex.sampler, tex.view, vk.IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + vk.UpdateDescriptorSet(b.dev, write) + } + for _, bind := range p.imgBinds { + tex := texBinds[bind] + write := vk.BuildWriteDescriptorSetImage(descSet, bind, vk.DESCRIPTOR_TYPE_STORAGE_IMAGE, 0, tex.view, vk.IMAGE_LAYOUT_GENERAL) + vk.UpdateDescriptorSet(b.dev, write) + } + for _, bind := range p.bufBinds { + buf := bufBinds[bind] + write := vk.BuildWriteDescriptorSetBuffer(descSet, bind, vk.DESCRIPTOR_TYPE_STORAGE_BUFFER, buf.buf) + vk.UpdateDescriptorSet(b.dev, write) + } + vk.CmdBindDescriptorSets(cmdBuf, bindPoint, p.layout, 0, []vk.DescriptorSet{descSet}) + break + } +} + +func (t *Texture) imageBarrier(cmdBuf vk.CommandBuffer, layout vk.ImageLayout, stage vk.PipelineStageFlags, access vk.AccessFlags) { + srcStage := t.scope.stage + if srcStage == 0 && t.layout == layout { + t.scope.stage = stage + t.scope.access = access + return + } + if srcStage == 0 { + srcStage = vk.PIPELINE_STAGE_TOP_OF_PIPE_BIT + } + b := vk.BuildImageMemoryBarrier( + t.img, + t.scope.access, access, + t.layout, layout, + ) + vk.CmdPipelineBarrier(cmdBuf, srcStage, stage, vk.DEPENDENCY_BY_REGION_BIT, nil, nil, []vk.ImageMemoryBarrier{b}) + t.layout = layout + t.scope.stage = stage + t.scope.access = access +} + +func (b *Backend) PrepareTexture(tex driver.Texture) { + t := tex.(*Texture) + cmdBuf := b.ensureCmdBuf() + t.imageBarrier(cmdBuf, + vk.IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + vk.PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + vk.ACCESS_SHADER_READ_BIT, + ) +} + +func (b *Backend) BindTexture(unit int, tex driver.Texture) { + t := tex.(*Texture) + b.desc.texBinds[unit] = t + b.desc.dirty = true +} + +func (b *Backend) BindPipeline(pipe driver.Pipeline) { + b.bindPipeline(pipe.(*Pipeline), vk.PIPELINE_BIND_POINT_GRAPHICS) +} + +func (b *Backend) BindProgram(prog driver.Program) { + b.bindPipeline(prog.(*Pipeline), vk.PIPELINE_BIND_POINT_COMPUTE) +} + +func (b *Backend) bindPipeline(p *Pipeline, point vk.PipelineBindPoint) { + b.pipe = p + b.desc.dirty = p.desc.descLayout != 0 + cmdBuf := b.currentCmdBuf() + vk.CmdBindPipeline(cmdBuf, point, p.pipe) +} + +func (s *Shader) Release() { + vk.DestroyShaderModule(s.dev, s.module) + *s = Shader{} +} + +func (b *Backend) BindStorageBuffer(binding int, buffer driver.Buffer) { + buf := buffer.(*Buffer) + b.desc.bufBinds[binding] = buf + b.desc.dirty = true + buf.barrier(b.currentCmdBuf(), + vk.PIPELINE_STAGE_COMPUTE_SHADER_BIT, + vk.ACCESS_SHADER_READ_BIT|vk.ACCESS_SHADER_WRITE_BIT, + ) +} + +func (b *Backend) BindUniforms(buffer driver.Buffer) { + buf := buffer.(*Buffer) + cmdBuf := b.currentCmdBuf() + for _, s := range b.pipe.pushRanges { + off := s.Offset() + vk.CmdPushConstants(cmdBuf, b.pipe.desc.layout, s.StageFlags(), off, buf.store[off:off+s.Size()]) + } +} + +func (b *Backend) BindVertexBuffer(buffer driver.Buffer, offset int) { + buf := buffer.(*Buffer) + cmdBuf := b.currentCmdBuf() + b.bindings = b.bindings[:0] + b.offsets = b.offsets[:0] + for i := 0; i < b.pipe.ninputs; i++ { + b.bindings = append(b.bindings, buf.buf) + b.offsets = append(b.offsets, vk.DeviceSize(offset)) + } + vk.CmdBindVertexBuffers(cmdBuf, 0, b.bindings, b.offsets) +} + +func (b *Backend) BindIndexBuffer(buffer driver.Buffer) { + buf := buffer.(*Buffer) + cmdBuf := b.currentCmdBuf() + vk.CmdBindIndexBuffer(cmdBuf, buf.buf, 0, vk.INDEX_TYPE_UINT16) +} + +func (b *Buffer) Download(data []byte) error { + if b.buf == 0 { + copy(data, b.store) + return nil + } + stage, mem, off := b.backend.stagingBuffer(len(data)) + cmdBuf := b.backend.ensureCmdBuf() + b.barrier(cmdBuf, + vk.PIPELINE_STAGE_TRANSFER_BIT, + vk.ACCESS_TRANSFER_READ_BIT, + ) + vk.CmdCopyBuffer(cmdBuf, b.buf, stage.buf, 0, off, len(data)) + stage.scope.stage = vk.PIPELINE_STAGE_TRANSFER_BIT + stage.scope.access = vk.ACCESS_TRANSFER_WRITE_BIT + stage.barrier(cmdBuf, + vk.PIPELINE_STAGE_HOST_BIT, + vk.ACCESS_HOST_READ_BIT, + ) + b.backend.submitCmdBuf(true) + copy(data, mem) + return nil +} + +func (b *Buffer) Upload(data []byte) { + if b.buf == 0 { + copy(b.store, data) + return + } + stage, mem, off := b.backend.stagingBuffer(len(data)) + copy(mem, data) + cmdBuf := b.backend.ensureCmdBuf() + b.barrier(cmdBuf, + vk.PIPELINE_STAGE_TRANSFER_BIT, + vk.ACCESS_TRANSFER_WRITE_BIT, + ) + vk.CmdCopyBuffer(cmdBuf, stage.buf, b.buf, off, 0, len(data)) + var access vk.AccessFlags + if b.usage&vk.BUFFER_USAGE_INDEX_BUFFER_BIT != 0 { + access |= vk.ACCESS_INDEX_READ_BIT + } + if b.usage&vk.BUFFER_USAGE_VERTEX_BUFFER_BIT != 0 { + access |= vk.ACCESS_VERTEX_ATTRIBUTE_READ_BIT + } + if access != 0 { + b.barrier(cmdBuf, + vk.PIPELINE_STAGE_VERTEX_INPUT_BIT, + access, + ) + } +} + +func (b *Buffer) barrier(cmdBuf vk.CommandBuffer, stage vk.PipelineStageFlags, access vk.AccessFlags) { + srcStage := b.scope.stage + if srcStage == 0 { + b.scope.stage = stage + b.scope.access = access + return + } + barrier := vk.BuildBufferMemoryBarrier( + b.buf, + b.scope.access, access, + ) + vk.CmdPipelineBarrier(cmdBuf, srcStage, stage, vk.DEPENDENCY_BY_REGION_BIT, nil, []vk.BufferMemoryBarrier{barrier}, nil) + b.scope.stage = stage + b.scope.access = access +} + +func (b *Buffer) Release() { + freeb := *b + if freeb.buf != 0 { + b.backend.deferFunc(func(d vk.Device) { + vk.DestroyBuffer(d, freeb.buf) + vk.FreeMemory(d, freeb.mem) + }) + } + *b = Buffer{} +} + +func (t *Texture) ReadPixels(src image.Rectangle, pixels []byte, stride int) error { + if len(pixels) == 0 { + return nil + } + sz := src.Size() + stageStride := sz.X * 4 + n := sz.Y * stageStride + stage, mem, off := t.backend.stagingBuffer(n) + cmdBuf := t.backend.ensureCmdBuf() + region := vk.BuildBufferImageCopy(off, stageStride/4, src.Min.X, src.Min.Y, sz.X, sz.Y) + t.imageBarrier(cmdBuf, + vk.IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + vk.PIPELINE_STAGE_TRANSFER_BIT, + vk.ACCESS_TRANSFER_READ_BIT, + ) + vk.CmdCopyImageToBuffer(cmdBuf, t.img, t.layout, stage.buf, []vk.BufferImageCopy{region}) + stage.scope.stage = vk.PIPELINE_STAGE_TRANSFER_BIT + stage.scope.access = vk.ACCESS_TRANSFER_WRITE_BIT + stage.barrier(cmdBuf, + vk.PIPELINE_STAGE_HOST_BIT, + vk.ACCESS_HOST_READ_BIT, + ) + t.backend.submitCmdBuf(true) + var srcOff, dstOff int + for y := 0; y < sz.Y; y++ { + dstRow := pixels[srcOff : srcOff+stageStride] + srcRow := mem[dstOff : dstOff+stageStride] + copy(dstRow, srcRow) + dstOff += stageStride + srcOff += stride + } + return nil +} + +func (b *Backend) currentCmdBuf() vk.CommandBuffer { + cur := b.cmdPool.current + if cur == nil { + panic("vulkan: invalid operation outside a render or compute pass") + } + return cur +} + +func (b *Backend) ensureCmdBuf() vk.CommandBuffer { + if b.cmdPool.current != nil { + return b.cmdPool.current + } + if b.cmdPool.used < len(b.cmdPool.buffers) { + buf := b.cmdPool.buffers[b.cmdPool.used] + b.cmdPool.current = buf + } else { + buf, err := vk.AllocateCommandBuffer(b.dev, b.cmdPool.pool) + if err != nil { + panic(err) + } + b.cmdPool.buffers = append(b.cmdPool.buffers, buf) + b.cmdPool.current = buf + } + b.cmdPool.used++ + buf := b.cmdPool.current + if err := vk.BeginCommandBuffer(buf); err != nil { + panic(err) + } + return buf +} + +func (b *Backend) BeginRenderPass(tex driver.Texture, d driver.LoadDesc) { + t := tex.(*Texture) + var vkop vk.AttachmentLoadOp + switch d.Action { + case driver.LoadActionClear: + vkop = vk.ATTACHMENT_LOAD_OP_CLEAR + case driver.LoadActionInvalidate: + vkop = vk.ATTACHMENT_LOAD_OP_DONT_CARE + case driver.LoadActionKeep: + vkop = vk.ATTACHMENT_LOAD_OP_LOAD + } + cmdBuf := b.ensureCmdBuf() + if sem := t.acquire; sem != 0 { + // The render pass targets a framebuffer that has an associated acquire semaphore. + // Wait for it by forming an execution barrier. + b.waitSems = append(b.waitSems, sem) + b.waitStages = append(b.waitStages, vk.PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT) + // But only for the first pass in a frame. + t.acquire = 0 + } + t.imageBarrier(cmdBuf, + vk.IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + vk.PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + vk.ACCESS_COLOR_ATTACHMENT_READ_BIT|vk.ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + ) + pass := b.lookupPass(t.format, vkop, t.layout, t.passLayout) + col := d.ClearColor + vk.CmdBeginRenderPass(cmdBuf, pass, t.fbo, t.width, t.height, [4]float32{col.R, col.G, col.B, col.A}) + t.layout = t.passLayout + // If the render pass describes an automatic image layout transition to its final layout, there + // is an implicit image barrier with destination PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT. Make + // sure any subsequent barrier includes the transition. + // See also https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VkSubpassDependency. + t.scope.stage |= vk.PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT +} + +func (b *Backend) EndRenderPass() { + vk.CmdEndRenderPass(b.cmdPool.current) +} + +func (b *Backend) BeginCompute() { + b.ensureCmdBuf() +} + +func (b *Backend) EndCompute() { +} + +func (b *Backend) lookupPass(fmt vk.Format, loadAct vk.AttachmentLoadOp, initLayout, finalLayout vk.ImageLayout) vk.RenderPass { + key := passKey{fmt: fmt, loadAct: loadAct, initLayout: initLayout, finalLayout: finalLayout} + if pass, ok := b.passes[key]; ok { + return pass + } + pass, err := vk.CreateRenderPass(b.dev, fmt, loadAct, initLayout, finalLayout, nil) + if err != nil { + panic(err) + } + b.passes[key] = pass + return pass +} + +func (b *Backend) submitCmdBuf(sync bool) { + buf := b.cmdPool.current + if buf == nil { + return + } + b.cmdPool.current = nil + if err := vk.EndCommandBuffer(buf); err != nil { + panic(err) + } + var fence vk.Fence + if sync { + fence = b.fence + } + if err := vk.QueueSubmit(b.queue, buf, b.waitSems, b.waitStages, b.sigSems, fence); err != nil { + panic(err) + } + b.waitSems = b.waitSems[:0] + b.sigSems = b.sigSems[:0] + b.waitStages = b.waitStages[:0] + if sync { + vk.WaitForFences(b.dev, b.fence) + vk.ResetFences(b.dev, b.fence) + } +} + +func (b *Backend) stagingBuffer(size int) (*Buffer, []byte, int) { + if b.staging.size+size > b.staging.cap { + if b.staging.buf != nil { + vk.UnmapMemory(b.dev, b.staging.buf.mem) + b.staging.buf.Release() + b.staging.cap = 0 + } + cap := 2 * (b.staging.size + size) + buf, err := b.newBuffer(cap, vk.BUFFER_USAGE_TRANSFER_SRC_BIT|vk.BUFFER_USAGE_TRANSFER_DST_BIT, + vk.MEMORY_PROPERTY_HOST_VISIBLE_BIT|vk.MEMORY_PROPERTY_HOST_COHERENT_BIT) + if err != nil { + panic(err) + } + mem, err := vk.MapMemory(b.dev, buf.mem, 0, cap) + if err != nil { + buf.Release() + panic(err) + } + b.staging.buf = buf + b.staging.mem = mem + b.staging.size = 0 + b.staging.cap = cap + } + off := b.staging.size + b.staging.size += size + mem := b.staging.mem[off : off+size] + return b.staging.buf, mem, off +} + +func formatFor(format driver.TextureFormat) vk.Format { + switch format { + case driver.TextureFormatRGBA8: + return vk.FORMAT_R8G8B8A8_UNORM + case driver.TextureFormatSRGBA: + return vk.FORMAT_R8G8B8A8_SRGB + case driver.TextureFormatFloat: + return vk.FORMAT_R16_SFLOAT + default: + panic("unsupported texture format") + } +} + +func mapErr(err error) error { + var vkErr vk.Error + if errors.As(err, &vkErr) && vkErr == vk.ERROR_DEVICE_LOST { + return driver.ErrDeviceLost + } + return err +} + +func (f *Texture) ImplementsRenderTarget() {} diff --git a/vendor/gioui.org/gpu/internal/vulkan/vulkan_nosupport.go b/vendor/gioui.org/gpu/internal/vulkan/vulkan_nosupport.go new file mode 100644 index 0000000..4364a43 --- /dev/null +++ b/vendor/gioui.org/gpu/internal/vulkan/vulkan_nosupport.go @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package vulkan + +// Empty file to avoid the build error for platforms without Vulkan support. diff --git a/vendor/gioui.org/gpu/pack.go b/vendor/gioui.org/gpu/pack.go index c4dbaad..3f8c925 100644 --- a/vendor/gioui.org/gpu/pack.go +++ b/vendor/gioui.org/gpu/pack.go @@ -9,8 +9,8 @@ import ( // packer packs a set of many smaller rectangles into // much fewer larger atlases. type packer struct { - maxDim int - spaces []image.Rectangle + maxDims image.Point + spaces []image.Rectangle sizes []image.Point pos image.Point @@ -41,45 +41,65 @@ func (p *packer) newPage() { p.sizes = append(p.sizes, image.Point{}) p.spaces = p.spaces[:0] p.spaces = append(p.spaces, image.Rectangle{ - Max: image.Point{X: p.maxDim, Y: p.maxDim}, + Max: image.Point{X: 1e6, Y: 1e6}, }) } func (p *packer) tryAdd(s image.Point) (placement, bool) { - // Go backwards to prioritize smaller spaces first. - for i := len(p.spaces) - 1; i >= 0; i-- { - space := p.spaces[i] + var ( + bestIdx = -1 + bestSpace image.Rectangle + bestSize = p.maxDims + ) + // Go backwards to prioritize smaller spaces. + for i, space := range p.spaces { rightSpace := space.Dx() - s.X bottomSpace := space.Dy() - s.Y - if rightSpace >= 0 && bottomSpace >= 0 { - // Remove space. - p.spaces[i] = p.spaces[len(p.spaces)-1] - p.spaces = p.spaces[:len(p.spaces)-1] - // Put s in the top left corner and add the (at most) - // two smaller spaces. - pos := space.Min - if bottomSpace > 0 { - p.spaces = append(p.spaces, image.Rectangle{ - Min: image.Point{X: pos.X, Y: pos.Y + s.Y}, - Max: image.Point{X: space.Max.X, Y: space.Max.Y}, - }) - } - if rightSpace > 0 { - p.spaces = append(p.spaces, image.Rectangle{ - Min: image.Point{X: pos.X + s.X, Y: pos.Y}, - Max: image.Point{X: space.Max.X, Y: pos.Y + s.Y}, - }) - } - idx := len(p.sizes) - 1 - size := &p.sizes[idx] - if x := pos.X + s.X; x > size.X { - size.X = x + if rightSpace < 0 || bottomSpace < 0 { + continue + } + idx := len(p.sizes) - 1 + size := p.sizes[idx] + if x := space.Min.X + s.X; x > size.X { + if x > p.maxDims.X { + continue } - if y := pos.Y + s.Y; y > size.Y { - size.Y = y + size.X = x + } + if y := space.Min.Y + s.Y; y > size.Y { + if y > p.maxDims.Y { + continue } - return placement{Idx: idx, Pos: pos}, true + size.Y = y } + if size.X*size.Y < bestSize.X*bestSize.Y { + bestIdx = i + bestSpace = space + bestSize = size + } + } + if bestIdx == -1 { + return placement{}, false + } + // Remove space. + p.spaces[bestIdx] = p.spaces[len(p.spaces)-1] + p.spaces = p.spaces[:len(p.spaces)-1] + // Put s in the top left corner and add the (at most) + // two smaller spaces. + pos := bestSpace.Min + if rem := bestSpace.Dy() - s.Y; rem > 0 { + p.spaces = append(p.spaces, image.Rectangle{ + Min: image.Point{X: pos.X, Y: pos.Y + s.Y}, + Max: image.Point{X: bestSpace.Max.X, Y: bestSpace.Max.Y}, + }) + } + if rem := bestSpace.Dx() - s.X; rem > 0 { + p.spaces = append(p.spaces, image.Rectangle{ + Min: image.Point{X: pos.X + s.X, Y: pos.Y}, + Max: image.Point{X: bestSpace.Max.X, Y: pos.Y + s.Y}, + }) } - return placement{}, false + idx := len(p.sizes) - 1 + p.sizes[idx] = bestSize + return placement{Idx: idx, Pos: pos}, true } diff --git a/vendor/gioui.org/gpu/path.go b/vendor/gioui.org/gpu/path.go index 2dd5875..5fd73cf 100644 --- a/vendor/gioui.org/gpu/path.go +++ b/vendor/gioui.org/gpu/path.go @@ -12,13 +12,15 @@ import ( "unsafe" "gioui.org/f32" - "gioui.org/gpu/backend" + "gioui.org/gpu/internal/driver" + "gioui.org/internal/byteslice" "gioui.org/internal/f32color" - gunsafe "gioui.org/internal/unsafe" + "gioui.org/shader" + "gioui.org/shader/gio" ) type pather struct { - ctx backend.Device + ctx driver.Device viewport image.Point @@ -27,28 +29,28 @@ type pather struct { } type coverer struct { - ctx backend.Device - prog [2]*program - texUniforms *coverTexUniforms - colUniforms *coverColUniforms - layout backend.InputLayout + ctx driver.Device + pipelines [3]*pipeline + texUniforms *coverTexUniforms + colUniforms *coverColUniforms + linearGradientUniforms *coverLinearGradientUniforms } type coverTexUniforms struct { - vert struct { - coverUniforms - _ [12]byte // Padding to multiple of 16. - } + coverUniforms + _ [12]byte // Padding to multiple of 16. } type coverColUniforms struct { - vert struct { - coverUniforms - _ [12]byte // Padding to multiple of 16. - } - frag struct { - colorUniforms - } + coverUniforms + _ [128 - unsafe.Sizeof(coverUniforms{}) - unsafe.Sizeof(colorUniforms{})]byte // Padding to 128 bytes. + colorUniforms +} + +type coverLinearGradientUniforms struct { + coverUniforms + _ [128 - unsafe.Sizeof(coverUniforms{}) - unsafe.Sizeof(gradientUniforms{})]byte // Padding to 128. + gradientUniforms } type coverUniforms struct { @@ -60,28 +62,24 @@ type coverUniforms struct { } type stenciler struct { - ctx backend.Device - prog struct { - prog *program + ctx driver.Device + pipeline struct { + pipeline *pipeline uniforms *stencilUniforms - layout backend.InputLayout } - iprog struct { - prog *program + ipipeline struct { + pipeline *pipeline uniforms *intersectUniforms - layout backend.InputLayout } fbos fboSet intersections fboSet - indexBuf backend.Buffer + indexBuf driver.Buffer } type stencilUniforms struct { - vert struct { - transform [4]float32 - pathOffset [2]float32 - _ [8]byte // Padding to multiple of 16. - } + transform [4]float32 + pathOffset [2]float32 + _ [8]byte // Padding to multiple of 16. } type intersectUniforms struct { @@ -97,13 +95,12 @@ type fboSet struct { type stencilFBO struct { size image.Point - fbo backend.Framebuffer - tex backend.Texture + tex driver.Texture } type pathData struct { ncurves int - data backend.Buffer + data driver.Buffer } // vertex data suitable for passing to vertex programs. @@ -132,10 +129,10 @@ const ( // Number of path quads per draw batch. pathBatchSize = 10000 // Size of a vertex as sent to gpu - vertStride = 7*4 + 2*2 + vertStride = 8 * 4 ) -func newPather(ctx backend.Device) *pather { +func newPather(ctx driver.Device) *pather { return &pather{ ctx: ctx, stenciler: newStenciler(ctx), @@ -143,25 +140,24 @@ func newPather(ctx backend.Device) *pather { } } -func newCoverer(ctx backend.Device) *coverer { +func newCoverer(ctx driver.Device) *coverer { c := &coverer{ ctx: ctx, } c.colUniforms = new(coverColUniforms) c.texUniforms = new(coverTexUniforms) - prog, layout, err := createColorPrograms(ctx, shader_cover_vert, shader_cover_frag, - [2]interface{}{&c.colUniforms.vert, &c.texUniforms.vert}, - [2]interface{}{&c.colUniforms.frag, nil}, + c.linearGradientUniforms = new(coverLinearGradientUniforms) + pipelines, err := createColorPrograms(ctx, gio.Shader_cover_vert, gio.Shader_cover_frag, + [3]interface{}{c.colUniforms, c.linearGradientUniforms, c.texUniforms}, ) if err != nil { panic(err) } - c.prog = prog - c.layout = layout + c.pipelines = pipelines return c } -func newStenciler(ctx backend.Device) *stenciler { +func newStenciler(ctx driver.Device) *stenciler { // Allocate a suitably large index buffer for drawing paths. indices := make([]uint16, pathBatchSize*6) for i := 0; i < pathBatchSize; i++ { @@ -173,51 +169,80 @@ func newStenciler(ctx backend.Device) *stenciler { indices[i*6+4] = i*4 + 1 indices[i*6+5] = i*4 + 3 } - indexBuf, err := ctx.NewImmutableBuffer(backend.BufferBindingIndices, gunsafe.BytesView(indices)) + indexBuf, err := ctx.NewImmutableBuffer(driver.BufferBindingIndices, byteslice.Slice(indices)) if err != nil { panic(err) } - progLayout, err := ctx.NewInputLayout(shader_stencil_vert, []backend.InputDesc{ - {Type: backend.DataTypeFloat, Size: 1, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).Corner))}, - {Type: backend.DataTypeFloat, Size: 1, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).MaxY))}, - {Type: backend.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).FromX))}, - {Type: backend.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).CtrlX))}, - {Type: backend.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).ToX))}, - }) - if err != nil { - panic(err) + progLayout := driver.VertexLayout{ + Inputs: []driver.InputDesc{ + {Type: shader.DataTypeFloat, Size: 1, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).Corner))}, + {Type: shader.DataTypeFloat, Size: 1, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).MaxY))}, + {Type: shader.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).FromX))}, + {Type: shader.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).CtrlX))}, + {Type: shader.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).ToX))}, + }, + Stride: vertStride, } - iprogLayout, err := ctx.NewInputLayout(shader_intersect_vert, []backend.InputDesc{ - {Type: backend.DataTypeFloat, Size: 2, Offset: 0}, - {Type: backend.DataTypeFloat, Size: 2, Offset: 4 * 2}, - }) - if err != nil { - panic(err) + iprogLayout := driver.VertexLayout{ + Inputs: []driver.InputDesc{ + {Type: shader.DataTypeFloat, Size: 2, Offset: 0}, + {Type: shader.DataTypeFloat, Size: 2, Offset: 4 * 2}, + }, + Stride: 4 * 4, } st := &stenciler{ ctx: ctx, indexBuf: indexBuf, } - prog, err := ctx.NewProgram(shader_stencil_vert, shader_stencil_frag) + vsh, fsh, err := newShaders(ctx, gio.Shader_stencil_vert, gio.Shader_stencil_frag) if err != nil { panic(err) } - st.prog.uniforms = new(stencilUniforms) - vertUniforms := newUniformBuffer(ctx, &st.prog.uniforms.vert) - st.prog.prog = newProgram(prog, vertUniforms, nil) - st.prog.layout = progLayout - iprog, err := ctx.NewProgram(shader_intersect_vert, shader_intersect_frag) + defer vsh.Release() + defer fsh.Release() + st.pipeline.uniforms = new(stencilUniforms) + vertUniforms := newUniformBuffer(ctx, st.pipeline.uniforms) + pipe, err := st.ctx.NewPipeline(driver.PipelineDesc{ + VertexShader: vsh, + FragmentShader: fsh, + VertexLayout: progLayout, + BlendDesc: driver.BlendDesc{ + Enable: true, + SrcFactor: driver.BlendFactorOne, + DstFactor: driver.BlendFactorOne, + }, + PixelFormat: driver.TextureFormatFloat, + Topology: driver.TopologyTriangles, + }) + st.pipeline.pipeline = &pipeline{pipe, vertUniforms} + if err != nil { + panic(err) + } + vsh, fsh, err = newShaders(ctx, gio.Shader_intersect_vert, gio.Shader_intersect_frag) if err != nil { panic(err) } - st.iprog.uniforms = new(intersectUniforms) - vertUniforms = newUniformBuffer(ctx, &st.iprog.uniforms.vert) - st.iprog.prog = newProgram(iprog, vertUniforms, nil) - st.iprog.layout = iprogLayout + defer vsh.Release() + defer fsh.Release() + st.ipipeline.uniforms = new(intersectUniforms) + vertUniforms = newUniformBuffer(ctx, &st.ipipeline.uniforms.vert) + ipipe, err := st.ctx.NewPipeline(driver.PipelineDesc{ + VertexShader: vsh, + FragmentShader: fsh, + VertexLayout: iprogLayout, + BlendDesc: driver.BlendDesc{ + Enable: true, + SrcFactor: driver.BlendFactorDstColor, + DstFactor: driver.BlendFactorZero, + }, + PixelFormat: driver.TextureFormatFloat, + Topology: driver.TopologyTriangleStrip, + }) + st.ipipeline.pipeline = &pipeline{ipipe, vertUniforms} return st } -func (s *fboSet) resize(ctx backend.Device, sizes []image.Point) { +func (s *fboSet) resize(ctx driver.Device, sizes []image.Point) { // Add fbos. for i := len(s.fbos); i < len(sizes); i++ { s.fbos = append(s.fbos, stencilFBO{}) @@ -231,38 +256,25 @@ func (s *fboSet) resize(ctx backend.Device, sizes []image.Point) { waste := float32(sz.X*sz.Y) / float32(f.size.X*f.size.Y) resize = resize || waste > 1.2 if resize { - if f.fbo != nil { - f.fbo.Release() + if f.tex != nil { f.tex.Release() } - tex, err := ctx.NewTexture(backend.TextureFormatFloat, sz.X, sz.Y, backend.FilterNearest, backend.FilterNearest, - backend.BufferBindingTexture|backend.BufferBindingFramebuffer) - if err != nil { - panic(err) - } - fbo, err := ctx.NewFramebuffer(tex, 0) + tex, err := ctx.NewTexture(driver.TextureFormatFloat, sz.X, sz.Y, driver.FilterNearest, driver.FilterNearest, + driver.BufferBindingTexture|driver.BufferBindingFramebuffer) if err != nil { panic(err) } f.size = sz f.tex = tex - f.fbo = fbo } } // Delete extra fbos. s.delete(ctx, len(sizes)) } -func (s *fboSet) invalidate(ctx backend.Device) { - for _, f := range s.fbos { - f.fbo.Invalidate() - } -} - -func (s *fboSet) delete(ctx backend.Device, idx int) { +func (s *fboSet) delete(ctx driver.Device, idx int) { for i := idx; i < len(s.fbos); i++ { f := s.fbos[i] - f.fbo.Release() f.tex.Release() } s.fbos = s.fbos[:idx] @@ -270,10 +282,9 @@ func (s *fboSet) delete(ctx backend.Device, idx int) { func (s *stenciler) release() { s.fbos.delete(s.ctx, 0) - s.prog.layout.Release() - s.prog.prog.Release() - s.iprog.layout.Release() - s.iprog.prog.Release() + s.intersections.delete(s.ctx, 0) + s.pipeline.pipeline.Release() + s.ipipeline.pipeline.Release() s.indexBuf.Release() } @@ -283,14 +294,13 @@ func (p *pather) release() { } func (c *coverer) release() { - for _, p := range c.prog { + for _, p := range c.pipelines { p.Release() } - c.layout.Release() } -func buildPath(ctx backend.Device, p []byte) pathData { - buf, err := ctx.NewImmutableBuffer(backend.BufferBindingVertices, p) +func buildPath(ctx driver.Device, p []byte) pathData { + buf, err := ctx.NewImmutableBuffer(driver.BufferBindingVertices, p) if err != nil { panic(err) } @@ -313,17 +323,10 @@ func (p *pather) stencilPath(bounds image.Rectangle, offset f32.Point, uv image. } func (s *stenciler) beginIntersect(sizes []image.Point) { - s.ctx.BlendFunc(backend.BlendFactorDstColor, backend.BlendFactorZero) // 8 bit coverage is enough, but OpenGL ES only supports single channel // floating point formats. Replace with GL_RGB+GL_UNSIGNED_BYTE if // no floating point support is available. s.intersections.resize(s.ctx, sizes) - s.ctx.BindProgram(s.iprog.prog.prog) -} - -func (s *stenciler) invalidateFBO() { - s.intersections.invalidate(s.ctx) - s.fbos.invalidate(s.ctx) } func (s *stenciler) cover(idx int) stencilFBO { @@ -331,11 +334,7 @@ func (s *stenciler) cover(idx int) stencilFBO { } func (s *stenciler) begin(sizes []image.Point) { - s.ctx.BlendFunc(backend.BlendFactorOne, backend.BlendFactorOne) s.fbos.resize(s.ctx, sizes) - s.ctx.BindProgram(s.prog.prog.prog) - s.ctx.BindInputLayout(s.prog.layout) - s.ctx.BindIndexBuffer(s.indexBuf) } func (s *stenciler) stencilPath(bounds image.Rectangle, offset f32.Point, uv image.Point, data pathData) { @@ -344,9 +343,9 @@ func (s *stenciler) stencilPath(bounds image.Rectangle, offset f32.Point, uv ima texSize := f32.Point{X: float32(bounds.Dx()), Y: float32(bounds.Dy())} scale := f32.Point{X: 2 / texSize.X, Y: 2 / texSize.Y} orig := f32.Point{X: -1 - float32(bounds.Min.X)*2/texSize.X, Y: -1 - float32(bounds.Min.Y)*2/texSize.Y} - s.prog.uniforms.vert.transform = [4]float32{scale.X, scale.Y, orig.X, orig.Y} - s.prog.uniforms.vert.pathOffset = [2]float32{offset.X, offset.Y} - s.prog.prog.UploadUniforms() + s.pipeline.uniforms.transform = [4]float32{scale.X, scale.Y, orig.X, orig.Y} + s.pipeline.uniforms.pathOffset = [2]float32{offset.X, offset.Y} + s.pipeline.pipeline.UploadUniforms(s.ctx) // Draw in batches that fit in uint16 indices. start := 0 nquads := data.ncurves / 4 @@ -356,35 +355,40 @@ func (s *stenciler) stencilPath(bounds image.Rectangle, offset f32.Point, uv ima batch = max } off := vertStride * start * 4 - s.ctx.BindVertexBuffer(data.data, vertStride, off) - s.ctx.DrawElements(backend.DrawModeTriangles, 0, batch*6) + s.ctx.BindVertexBuffer(data.data, off) + s.ctx.DrawElements(0, batch*6) start += batch } } -func (p *pather) cover(z float32, mat materialType, col f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D, coverScale, coverOff f32.Point) { - p.coverer.cover(z, mat, col, scale, off, uvTrans, coverScale, coverOff) +func (p *pather) cover(mat materialType, col f32color.RGBA, col1, col2 f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D, coverScale, coverOff f32.Point) { + p.coverer.cover(mat, col, col1, col2, scale, off, uvTrans, coverScale, coverOff) } -func (c *coverer) cover(z float32, mat materialType, col f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D, coverScale, coverOff f32.Point) { - p := c.prog[mat] - c.ctx.BindProgram(p.prog) +func (c *coverer) cover(mat materialType, col f32color.RGBA, col1, col2 f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D, coverScale, coverOff f32.Point) { var uniforms *coverUniforms switch mat { case materialColor: - c.colUniforms.frag.color = col - uniforms = &c.colUniforms.vert.coverUniforms + c.colUniforms.color = col + uniforms = &c.colUniforms.coverUniforms + case materialLinearGradient: + c.linearGradientUniforms.color1 = col1 + c.linearGradientUniforms.color2 = col2 + + t1, t2, t3, t4, t5, t6 := uvTrans.Elems() + c.linearGradientUniforms.uvTransformR1 = [4]float32{t1, t2, t3, 0} + c.linearGradientUniforms.uvTransformR2 = [4]float32{t4, t5, t6, 0} + uniforms = &c.linearGradientUniforms.coverUniforms case materialTexture: t1, t2, t3, t4, t5, t6 := uvTrans.Elems() - c.texUniforms.vert.uvTransformR1 = [4]float32{t1, t2, t3, 0} - c.texUniforms.vert.uvTransformR2 = [4]float32{t4, t5, t6, 0} - uniforms = &c.texUniforms.vert.coverUniforms + c.texUniforms.uvTransformR1 = [4]float32{t1, t2, t3, 0} + c.texUniforms.uvTransformR2 = [4]float32{t4, t5, t6, 0} + uniforms = &c.texUniforms.coverUniforms } - uniforms.z = z uniforms.transform = [4]float32{scale.X, scale.Y, off.X, off.Y} uniforms.uvCoverTransform = [4]float32{coverScale.X, coverScale.Y, coverOff.X, coverOff.Y} - p.UploadUniforms() - c.ctx.DrawArrays(backend.DrawModeTriangleStrip, 0, 4) + c.pipelines[mat].UploadUniforms(c.ctx) + c.ctx.DrawArrays(0, 4) } func init() { diff --git a/vendor/gioui.org/gpu/shaders.go b/vendor/gioui.org/gpu/shaders.go deleted file mode 100644 index d8e8435..0000000 --- a/vendor/gioui.org/gpu/shaders.go +++ /dev/null @@ -1,657 +0,0 @@ -// Code generated by build.go. DO NOT EDIT. - -package gpu - -import "gioui.org/gpu/backend" - -var ( - shader_blit_frag = [...]backend.ShaderSources{ - { - Uniforms: backend.UniformsReflection{ - Blocks: []backend.UniformBlock{{Name: "Color", Binding: 0}}, - Locations: []backend.UniformLocation{{Name: "_12._color", Type: 0x0, Size: 4, Offset: 0}}, - Size: 16, - }, - GLSL100ES: "precision mediump float;\nprecision highp int;\n\nstruct Color\n{\n vec4 _color;\n};\n\nuniform Color _12;\n\nvarying vec2 vUV;\n\nvoid main()\n{\n gl_FragData[0] = _12._color;\n}\n\n", - GLSL300ES: "#version 300 es\nprecision mediump float;\nprecision highp int;\n\nlayout(std140) uniform Color\n{\n vec4 _color;\n} _12;\n\nlayout(location = 0) out vec4 fragColor;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = _12._color;\n}\n\n", - GLSL130: "#version 130\n\nstruct Color\n{\n vec4 _color;\n};\n\nuniform Color _12;\n\nout vec4 fragColor;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = _12._color;\n}\n\n", - GLSL150: "#version 150\n\nstruct Color\n{\n vec4 _color;\n};\n\nuniform Color _12;\n\nout vec4 fragColor;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = _12._color;\n}\n\n", - /* - cbuffer Color : register(b0) - { - float4 _12_color : packoffset(c0); - }; - - - static float4 fragColor; - static float2 vUV; - - struct SPIRV_Cross_Input - { - float2 vUV : TEXCOORD0; - }; - - struct SPIRV_Cross_Output - { - float4 fragColor : SV_Target0; - }; - - void frag_main() - { - fragColor = _12_color; - } - - SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) - { - vUV = stage_input.vUV; - frag_main(); - SPIRV_Cross_Output stage_output; - stage_output.fragColor = fragColor; - return stage_output; - } - - */ - HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0xc6, 0x4a, 0x23, 0x81, 0x87, 0xab, 0xe3, 0xca, 0x12, 0x9, 0x7e, 0x2f, 0x5e, 0x2, 0x62, 0x14, 0x1, 0x0, 0x0, 0x0, 0x70, 0x2, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x84, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x0, 0x48, 0x1, 0x0, 0x0, 0x8, 0x2, 0x0, 0x0, 0x3c, 0x2, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0x44, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x14, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x1, 0x0, 0x24, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x1, 0x0, 0x0, 0x2, 0x0, 0x8, 0xf, 0x80, 0x0, 0x0, 0xe4, 0xa0, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0x40, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x59, 0x0, 0x0, 0x4, 0x46, 0x8e, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x6, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x8e, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0xb8, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0x0, 0x1, 0x0, 0x0, 0x90, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x0, 0xab, 0xab, 0x3c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x31, 0x32, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x0, 0xab, 0xab, 0x1, 0x0, 0x3, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0x49, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0xab, 0xab, 0xab, 0x4f, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x0, 0xab, 0xab}, - }, - { - Textures: []backend.TextureBinding{{Name: "tex", Binding: 0}}, - GLSL100ES: "precision mediump float;\nprecision highp int;\n\nuniform mediump sampler2D tex;\n\nvarying vec2 vUV;\n\nvoid main()\n{\n gl_FragData[0] = texture2D(tex, vUV);\n}\n\n", - GLSL300ES: "#version 300 es\nprecision mediump float;\nprecision highp int;\n\nuniform mediump sampler2D tex;\n\nlayout(location = 0) out vec4 fragColor;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = texture(tex, vUV);\n}\n\n", - GLSL130: "#version 130\n\nuniform sampler2D tex;\n\nout vec4 fragColor;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = texture(tex, vUV);\n}\n\n", - GLSL150: "#version 150\n\nuniform sampler2D tex;\n\nout vec4 fragColor;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = texture(tex, vUV);\n}\n\n", - /* - Texture2D<float4> tex : register(t0); - SamplerState _tex_sampler : register(s0); - - static float4 fragColor; - static float2 vUV; - - struct SPIRV_Cross_Input - { - float2 vUV : TEXCOORD0; - }; - - struct SPIRV_Cross_Output - { - float4 fragColor : SV_Target0; - }; - - void frag_main() - { - fragColor = tex.Sample(_tex_sampler, vUV); - } - - SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) - { - vUV = stage_input.vUV; - frag_main(); - SPIRV_Cross_Output stage_output; - stage_output.fragColor = fragColor; - return stage_output; - } - - */ - HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0xb7, 0x3f, 0x1d, 0xb1, 0x80, 0xcd, 0x80, 0xa3, 0x57, 0x9, 0xfb, 0x5a, 0x9f, 0x56, 0xd6, 0xda, 0x1, 0x0, 0x0, 0x0, 0x94, 0x2, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0xa4, 0x0, 0x0, 0x0, 0x10, 0x1, 0x0, 0x0, 0x8c, 0x1, 0x0, 0x0, 0x2c, 0x2, 0x0, 0x0, 0x60, 0x2, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0x64, 0x0, 0x0, 0x0, 0x64, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x3c, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x28, 0x0, 0x1, 0x0, 0x24, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x3, 0xb0, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x90, 0x0, 0x8, 0xf, 0xa0, 0x42, 0x0, 0x0, 0x3, 0x0, 0x0, 0xf, 0x80, 0x0, 0x0, 0xe4, 0xb0, 0x0, 0x8, 0xe4, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x8, 0xf, 0x80, 0x0, 0x0, 0xe4, 0x80, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0x64, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x19, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x3, 0x0, 0x60, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x58, 0x18, 0x0, 0x4, 0x0, 0x70, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x55, 0x55, 0x0, 0x0, 0x62, 0x10, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x45, 0x0, 0x0, 0x9, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x7e, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x60, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0x98, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0x0, 0x1, 0x0, 0x0, 0x6d, 0x0, 0x0, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x69, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xd, 0x0, 0x0, 0x0, 0x5f, 0x74, 0x65, 0x78, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x0, 0x74, 0x65, 0x78, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0xab, 0xab, 0xab, 0x49, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0xab, 0xab, 0xab, 0x4f, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x0, 0xab, 0xab}, - }, - } - shader_blit_vert = backend.ShaderSources{ - Inputs: []backend.InputLocation{{Name: "pos", Location: 0, Semantic: "POSITION", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "uv", Location: 1, Semantic: "NORMAL", SemanticIndex: 0, Type: 0x0, Size: 2}}, - Uniforms: backend.UniformsReflection{ - Blocks: []backend.UniformBlock{{Name: "Block", Binding: 0}}, - Locations: []backend.UniformLocation{{Name: "_52.transform", Type: 0x0, Size: 4, Offset: 0}, {Name: "_52.uvTransformR1", Type: 0x0, Size: 4, Offset: 16}, {Name: "_52.uvTransformR2", Type: 0x0, Size: 4, Offset: 32}, {Name: "_52.z", Type: 0x0, Size: 1, Offset: 48}}, - Size: 52, - }, - GLSL100ES: "\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 transform;\n vec4 uvTransformR1;\n vec4 uvTransformR2;\n float z;\n};\n\nuniform Block _52;\n\nattribute vec2 pos;\nvarying vec2 vUV;\nattribute vec2 uv;\n\nvec4 toClipSpace(vec4 pos_1)\n{\n return pos_1;\n}\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n vec2 p = (pos * _52.transform.xy) + _52.transform.zw;\n vec4 param = vec4(p, _52.z, 1.0);\n gl_Position = toClipSpace(param);\n m3x2 param_1 = m3x2(_52.uvTransformR1.xyz, _52.uvTransformR2.xyz);\n vec3 param_2 = vec3(uv, 1.0);\n vUV = transform3x2(param_1, param_2).xy;\n}\n\n", - GLSL300ES: "#version 300 es\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nlayout(std140) uniform Block\n{\n vec4 transform;\n vec4 uvTransformR1;\n vec4 uvTransformR2;\n float z;\n} _52;\n\nlayout(location = 0) in vec2 pos;\nout vec2 vUV;\nlayout(location = 1) in vec2 uv;\n\nvec4 toClipSpace(vec4 pos_1)\n{\n return pos_1;\n}\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n vec2 p = (pos * _52.transform.xy) + _52.transform.zw;\n vec4 param = vec4(p, _52.z, 1.0);\n gl_Position = toClipSpace(param);\n m3x2 param_1 = m3x2(_52.uvTransformR1.xyz, _52.uvTransformR2.xyz);\n vec3 param_2 = vec3(uv, 1.0);\n vUV = transform3x2(param_1, param_2).xy;\n}\n\n", - GLSL130: "#version 130\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 transform;\n vec4 uvTransformR1;\n vec4 uvTransformR2;\n float z;\n};\n\nuniform Block _52;\n\nin vec2 pos;\nout vec2 vUV;\nin vec2 uv;\n\nvec4 toClipSpace(vec4 pos_1)\n{\n return pos_1;\n}\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n vec2 p = (pos * _52.transform.xy) + _52.transform.zw;\n vec4 param = vec4(p, _52.z, 1.0);\n gl_Position = toClipSpace(param);\n m3x2 param_1 = m3x2(_52.uvTransformR1.xyz, _52.uvTransformR2.xyz);\n vec3 param_2 = vec3(uv, 1.0);\n vUV = transform3x2(param_1, param_2).xy;\n}\n\n", - GLSL150: "#version 150\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 transform;\n vec4 uvTransformR1;\n vec4 uvTransformR2;\n float z;\n};\n\nuniform Block _52;\n\nin vec2 pos;\nout vec2 vUV;\nin vec2 uv;\n\nvec4 toClipSpace(vec4 pos_1)\n{\n return pos_1;\n}\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n vec2 p = (pos * _52.transform.xy) + _52.transform.zw;\n vec4 param = vec4(p, _52.z, 1.0);\n gl_Position = toClipSpace(param);\n m3x2 param_1 = m3x2(_52.uvTransformR1.xyz, _52.uvTransformR2.xyz);\n vec3 param_2 = vec3(uv, 1.0);\n vUV = transform3x2(param_1, param_2).xy;\n}\n\n", - /* - struct m3x2 - { - float3 r0; - float3 r1; - }; - - static const m3x2 _116 = { float3(1.0f, 0.0f, 0.0f), float3(0.0f, -1.0f, 1.0f) }; - static const m3x2 _118 = { float3(1.0f, 0.0f, 0.0f), float3(0.0f, 1.0f, 0.0f) }; - - cbuffer Block : register(b0) - { - float4 _69_transform : packoffset(c0); - float4 _69_uvTransformR1 : packoffset(c1); - float4 _69_uvTransformR2 : packoffset(c2); - float _69_z : packoffset(c3); - }; - - - static float4 gl_Position; - static float2 pos; - static float2 vUV; - static float2 uv; - - struct SPIRV_Cross_Input - { - float2 pos : POSITION; - float2 uv : NORMAL; - }; - - struct SPIRV_Cross_Output - { - float2 vUV : TEXCOORD0; - float4 gl_Position : SV_Position; - }; - - float4 toClipSpace(float4 pos_1) - { - return float4(pos_1.xy, (pos_1.z + pos_1.w) * 0.5f, pos_1.w); - } - - float3 transform3x2(m3x2 t, float3 v) - { - return float3(dot(t.r0, v), dot(t.r1, v), dot(float3(0.0f, 0.0f, 1.0f), v)); - } - - void vert_main() - { - float2 p = (pos * _69_transform.xy) + _69_transform.zw; - float4 param = float4(p, _69_z, 1.0f); - gl_Position = toClipSpace(param); - m3x2 _103 = { _69_uvTransformR1.xyz, _69_uvTransformR2.xyz }; - m3x2 param_1 = _103; - float3 param_2 = float3(uv, 1.0f); - vUV = transform3x2(param_1, param_2).xy; - } - - SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) - { - pos = stage_input.pos; - uv = stage_input.uv; - vert_main(); - SPIRV_Cross_Output stage_output; - stage_output.gl_Position = gl_Position; - stage_output.vUV = vUV; - return stage_output; - } - - */ - HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0x57, 0x0, 0x34, 0xa, 0xf6, 0x3b, 0x1b, 0xc4, 0x28, 0xa1, 0x26, 0x75, 0x95, 0x95, 0xc4, 0x27, 0x1, 0x0, 0x0, 0x0, 0xc0, 0x4, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x24, 0x1, 0x0, 0x0, 0x54, 0x2, 0x0, 0x0, 0xd0, 0x2, 0x0, 0x0, 0x18, 0x4, 0x0, 0x0, 0x68, 0x4, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0xe4, 0x0, 0x0, 0x0, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0xb0, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x1, 0x0, 0x24, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x24, 0x0, 0x1, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0x51, 0x0, 0x0, 0x5, 0x5, 0x0, 0xf, 0xa0, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x0, 0x80, 0x0, 0x0, 0xf, 0x90, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x1, 0x80, 0x1, 0x0, 0xf, 0x90, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x7, 0x80, 0x1, 0x0, 0xc4, 0x90, 0x5, 0x0, 0xd0, 0xa0, 0x5, 0x0, 0xc5, 0xa0, 0x8, 0x0, 0x0, 0x3, 0x0, 0x0, 0x1, 0xe0, 0x2, 0x0, 0xe4, 0xa0, 0x0, 0x0, 0xe4, 0x80, 0x8, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0xe0, 0x3, 0x0, 0xe4, 0xa0, 0x0, 0x0, 0xe4, 0x80, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 0xe4, 0x90, 0x1, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0xee, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x3, 0xc0, 0x0, 0x0, 0xe4, 0x80, 0x0, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0x7, 0x80, 0x5, 0x0, 0xe4, 0xa0, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0xc, 0xc0, 0x4, 0x0, 0x0, 0xa0, 0x0, 0x0, 0x64, 0x80, 0x0, 0x0, 0x24, 0x80, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0x28, 0x1, 0x0, 0x0, 0x40, 0x0, 0x1, 0x0, 0x4a, 0x0, 0x0, 0x0, 0x59, 0x0, 0x0, 0x4, 0x46, 0x8e, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0x32, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x67, 0x0, 0x0, 0x4, 0xf2, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x2, 0x1, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x32, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x42, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x10, 0x0, 0x0, 0x8, 0x12, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x82, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x46, 0x2, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x8, 0x22, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x82, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x46, 0x2, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xb, 0x32, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6, 0x8a, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xa, 0x42, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x36, 0x0, 0x0, 0x5, 0x82, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0x40, 0x1, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xfe, 0xff, 0x0, 0x1, 0x0, 0x0, 0x18, 0x1, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x0, 0xab, 0xab, 0x3c, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdc, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xee, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x36, 0x39, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x0, 0xab, 0xab, 0x1, 0x0, 0x3, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x36, 0x39, 0x5f, 0x75, 0x76, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x52, 0x31, 0x0, 0x5f, 0x36, 0x39, 0x5f, 0x75, 0x76, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x52, 0x32, 0x0, 0x5f, 0x36, 0x39, 0x5f, 0x7a, 0x0, 0xab, 0xab, 0x0, 0x0, 0x3, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0x49, 0x53, 0x47, 0x4e, 0x48, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x0, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x0, 0x4f, 0x53, 0x47, 0x4e, 0x50, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xc, 0x0, 0x0, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0x53, 0x56, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x0, 0xab, 0xab, 0xab}, - } - shader_cover_frag = [...]backend.ShaderSources{ - { - Uniforms: backend.UniformsReflection{ - Blocks: []backend.UniformBlock{{Name: "Color", Binding: 0}}, - Locations: []backend.UniformLocation{{Name: "_12._color", Type: 0x0, Size: 4, Offset: 0}}, - Size: 16, - }, - Textures: []backend.TextureBinding{{Name: "cover", Binding: 1}}, - GLSL100ES: "precision mediump float;\nprecision highp int;\n\nstruct Color\n{\n vec4 _color;\n};\n\nuniform Color _12;\n\nuniform mediump sampler2D cover;\n\nvarying highp vec2 vCoverUV;\nvarying vec2 vUV;\n\nvoid main()\n{\n gl_FragData[0] = _12._color;\n float cover_1 = abs(texture2D(cover, vCoverUV).x);\n gl_FragData[0] *= cover_1;\n}\n\n", - GLSL300ES: "#version 300 es\nprecision mediump float;\nprecision highp int;\n\nlayout(std140) uniform Color\n{\n vec4 _color;\n} _12;\n\nuniform mediump sampler2D cover;\n\nlayout(location = 0) out vec4 fragColor;\nin highp vec2 vCoverUV;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = _12._color;\n float cover_1 = abs(texture(cover, vCoverUV).x);\n fragColor *= cover_1;\n}\n\n", - GLSL130: "#version 130\n\nstruct Color\n{\n vec4 _color;\n};\n\nuniform Color _12;\n\nuniform sampler2D cover;\n\nout vec4 fragColor;\nin vec2 vCoverUV;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = _12._color;\n float cover_1 = abs(texture(cover, vCoverUV).x);\n fragColor *= cover_1;\n}\n\n", - GLSL150: "#version 150\n\nstruct Color\n{\n vec4 _color;\n};\n\nuniform Color _12;\n\nuniform sampler2D cover;\n\nout vec4 fragColor;\nin vec2 vCoverUV;\nin vec2 vUV;\n\nvoid main()\n{\n fragColor = _12._color;\n float cover_1 = abs(texture(cover, vCoverUV).x);\n fragColor *= cover_1;\n}\n\n", - /* - cbuffer Color : register(b0) - { - float4 _12_color : packoffset(c0); - }; - - Texture2D<float4> cover : register(t1); - SamplerState _cover_sampler : register(s1); - - static float4 fragColor; - static float2 vCoverUV; - static float2 vUV; - - struct SPIRV_Cross_Input - { - float2 vCoverUV : TEXCOORD0; - float2 vUV : TEXCOORD1; - }; - - struct SPIRV_Cross_Output - { - float4 fragColor : SV_Target0; - }; - - void frag_main() - { - fragColor = _12_color; - float cover_1 = abs(cover.Sample(_cover_sampler, vCoverUV).x); - fragColor *= cover_1; - } - - SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) - { - vCoverUV = stage_input.vCoverUV; - vUV = stage_input.vUV; - frag_main(); - SPIRV_Cross_Output stage_output; - stage_output.fragColor = fragColor; - return stage_output; - } - - */ - HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0x94, 0xe9, 0xc3, 0x12, 0xd8, 0xfb, 0xb, 0x3b, 0xe, 0xda, 0x43, 0x25, 0xcb, 0x53, 0x1c, 0x9d, 0x1, 0x0, 0x0, 0x0, 0x84, 0x3, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x0, 0x74, 0x1, 0x0, 0x0, 0xf0, 0x1, 0x0, 0x0, 0x4, 0x3, 0x0, 0x0, 0x50, 0x3, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0x8c, 0x0, 0x0, 0x0, 0x8c, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x58, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x1, 0x0, 0x28, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x34, 0x0, 0x1, 0x0, 0x24, 0x0, 0x0, 0x0, 0x34, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0xf, 0xb0, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x90, 0x0, 0x8, 0xf, 0xa0, 0x42, 0x0, 0x0, 0x3, 0x0, 0x0, 0xf, 0x80, 0x0, 0x0, 0xe4, 0xb0, 0x0, 0x8, 0xe4, 0xa0, 0x23, 0x0, 0x0, 0x2, 0x0, 0x0, 0x1, 0x80, 0x0, 0x0, 0x0, 0x80, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0xf, 0x80, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x8, 0xf, 0x80, 0x0, 0x0, 0xe4, 0x80, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0xa0, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x59, 0x0, 0x0, 0x4, 0x46, 0x8e, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x3, 0x0, 0x60, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x58, 0x18, 0x0, 0x4, 0x0, 0x70, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x55, 0x55, 0x0, 0x0, 0x62, 0x10, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x2, 0x1, 0x0, 0x0, 0x0, 0x45, 0x0, 0x0, 0x9, 0xf2, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x7e, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x60, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x9, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x10, 0x80, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x8e, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0xc, 0x1, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x98, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0x0, 0x1, 0x0, 0x0, 0xe4, 0x0, 0x0, 0x0, 0x7c, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8b, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xd, 0x0, 0x0, 0x0, 0x91, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x5f, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x0, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x0, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x0, 0xab, 0x91, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xb0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xd4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x31, 0x32, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x0, 0xab, 0xab, 0x1, 0x0, 0x3, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0x49, 0x53, 0x47, 0x4e, 0x44, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0xab, 0xab, 0xab, 0x4f, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x0, 0xab, 0xab}, - }, - { - Textures: []backend.TextureBinding{{Name: "tex", Binding: 0}, {Name: "cover", Binding: 1}}, - GLSL100ES: "precision mediump float;\nprecision highp int;\n\nuniform mediump sampler2D tex;\nuniform mediump sampler2D cover;\n\nvarying vec2 vUV;\nvarying highp vec2 vCoverUV;\n\nvoid main()\n{\n gl_FragData[0] = texture2D(tex, vUV);\n float cover_1 = abs(texture2D(cover, vCoverUV).x);\n gl_FragData[0] *= cover_1;\n}\n\n", - GLSL300ES: "#version 300 es\nprecision mediump float;\nprecision highp int;\n\nuniform mediump sampler2D tex;\nuniform mediump sampler2D cover;\n\nlayout(location = 0) out vec4 fragColor;\nin vec2 vUV;\nin highp vec2 vCoverUV;\n\nvoid main()\n{\n fragColor = texture(tex, vUV);\n float cover_1 = abs(texture(cover, vCoverUV).x);\n fragColor *= cover_1;\n}\n\n", - GLSL130: "#version 130\n\nuniform sampler2D tex;\nuniform sampler2D cover;\n\nout vec4 fragColor;\nin vec2 vUV;\nin vec2 vCoverUV;\n\nvoid main()\n{\n fragColor = texture(tex, vUV);\n float cover_1 = abs(texture(cover, vCoverUV).x);\n fragColor *= cover_1;\n}\n\n", - GLSL150: "#version 150\n\nuniform sampler2D tex;\nuniform sampler2D cover;\n\nout vec4 fragColor;\nin vec2 vUV;\nin vec2 vCoverUV;\n\nvoid main()\n{\n fragColor = texture(tex, vUV);\n float cover_1 = abs(texture(cover, vCoverUV).x);\n fragColor *= cover_1;\n}\n\n", - /* - Texture2D<float4> tex : register(t0); - SamplerState _tex_sampler : register(s0); - Texture2D<float4> cover : register(t1); - SamplerState _cover_sampler : register(s1); - - static float4 fragColor; - static float2 vUV; - static float2 vCoverUV; - - struct SPIRV_Cross_Input - { - float2 vCoverUV : TEXCOORD0; - float2 vUV : TEXCOORD1; - }; - - struct SPIRV_Cross_Output - { - float4 fragColor : SV_Target0; - }; - - void frag_main() - { - fragColor = tex.Sample(_tex_sampler, vUV); - float cover_1 = abs(cover.Sample(_cover_sampler, vCoverUV).x); - fragColor *= cover_1; - } - - SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) - { - vUV = stage_input.vUV; - vCoverUV = stage_input.vCoverUV; - frag_main(); - SPIRV_Cross_Output stage_output; - stage_output.fragColor = fragColor; - return stage_output; - } - - */ - HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0xcb, 0xa0, 0xb0, 0x5b, 0x6e, 0x48, 0xa6, 0x5a, 0x1c, 0x34, 0x50, 0xfe, 0xd4, 0x6f, 0x72, 0xb5, 0x1, 0x0, 0x0, 0x0, 0xbc, 0x3, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0xec, 0x0, 0x0, 0x0, 0xcc, 0x1, 0x0, 0x0, 0x48, 0x2, 0x0, 0x0, 0x3c, 0x3, 0x0, 0x0, 0x88, 0x3, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0xac, 0x0, 0x0, 0x0, 0xac, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x80, 0x0, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x2c, 0x0, 0x2, 0x0, 0x24, 0x0, 0x0, 0x0, 0x2c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x0, 0x0, 0x2, 0xff, 0xff, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0xf, 0xb0, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x90, 0x0, 0x8, 0xf, 0xa0, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x90, 0x1, 0x8, 0xf, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 0x1b, 0xb0, 0x42, 0x0, 0x0, 0x3, 0x0, 0x0, 0xf, 0x80, 0x0, 0x0, 0xe4, 0x80, 0x0, 0x8, 0xe4, 0xa0, 0x42, 0x0, 0x0, 0x3, 0x1, 0x0, 0xf, 0x80, 0x0, 0x0, 0xe4, 0xb0, 0x1, 0x8, 0xe4, 0xa0, 0x23, 0x0, 0x0, 0x2, 0x1, 0x0, 0x1, 0x80, 0x1, 0x0, 0x0, 0x80, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0xf, 0x80, 0x0, 0x0, 0xe4, 0x80, 0x1, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0x2, 0x0, 0x8, 0xf, 0x80, 0x0, 0x0, 0xe4, 0x80, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0xd8, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x3, 0x0, 0x60, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x3, 0x0, 0x60, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x58, 0x18, 0x0, 0x4, 0x0, 0x70, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x55, 0x55, 0x0, 0x0, 0x58, 0x18, 0x0, 0x4, 0x0, 0x70, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x55, 0x55, 0x0, 0x0, 0x62, 0x10, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x62, 0x10, 0x0, 0x3, 0xc2, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x2, 0x2, 0x0, 0x0, 0x0, 0x45, 0x0, 0x0, 0x9, 0xf2, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6, 0x1a, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x7e, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x60, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x45, 0x0, 0x0, 0x9, 0xf2, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x7e, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x60, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x8, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0xe, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x10, 0x80, 0x81, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0xec, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0x0, 0x1, 0x0, 0x0, 0xc2, 0x0, 0x0, 0x0, 0x9c, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa9, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xb8, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xd, 0x0, 0x0, 0x0, 0xbc, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xd, 0x0, 0x0, 0x0, 0x5f, 0x74, 0x65, 0x78, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x0, 0x5f, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x0, 0x74, 0x65, 0x78, 0x0, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0xab, 0xab, 0x49, 0x53, 0x47, 0x4e, 0x44, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0xc, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0xab, 0xab, 0xab, 0x4f, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x0, 0xab, 0xab}, - }, - } - shader_cover_vert = backend.ShaderSources{ - Inputs: []backend.InputLocation{{Name: "pos", Location: 0, Semantic: "POSITION", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "uv", Location: 1, Semantic: "NORMAL", SemanticIndex: 0, Type: 0x0, Size: 2}}, - Uniforms: backend.UniformsReflection{ - Blocks: []backend.UniformBlock{{Name: "Block", Binding: 0}}, - Locations: []backend.UniformLocation{{Name: "_53.transform", Type: 0x0, Size: 4, Offset: 0}, {Name: "_53.uvCoverTransform", Type: 0x0, Size: 4, Offset: 16}, {Name: "_53.uvTransformR1", Type: 0x0, Size: 4, Offset: 32}, {Name: "_53.uvTransformR2", Type: 0x0, Size: 4, Offset: 48}, {Name: "_53.z", Type: 0x0, Size: 1, Offset: 64}}, - Size: 68, - }, - GLSL100ES: "\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 transform;\n vec4 uvCoverTransform;\n vec4 uvTransformR1;\n vec4 uvTransformR2;\n float z;\n};\n\nuniform Block _53;\n\nattribute vec2 pos;\nvarying vec2 vUV;\nattribute vec2 uv;\nvarying vec2 vCoverUV;\n\nvec4 toClipSpace(vec4 pos_1)\n{\n return pos_1;\n}\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n vec4 param = vec4((pos * _53.transform.xy) + _53.transform.zw, _53.z, 1.0);\n gl_Position = toClipSpace(param);\n m3x2 param_1 = m3x2(_53.uvTransformR1.xyz, _53.uvTransformR2.xyz);\n vec3 param_2 = vec3(uv, 1.0);\n vUV = transform3x2(param_1, param_2).xy;\n m3x2 param_3 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_4 = vec3(uv, 1.0);\n vec3 uv3 = transform3x2(param_3, param_4);\n vCoverUV = ((uv3 * vec3(_53.uvCoverTransform.xy, 1.0)) + vec3(_53.uvCoverTransform.zw, 0.0)).xy;\n}\n\n", - GLSL300ES: "#version 300 es\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nlayout(std140) uniform Block\n{\n vec4 transform;\n vec4 uvCoverTransform;\n vec4 uvTransformR1;\n vec4 uvTransformR2;\n float z;\n} _53;\n\nlayout(location = 0) in vec2 pos;\nout vec2 vUV;\nlayout(location = 1) in vec2 uv;\nout vec2 vCoverUV;\n\nvec4 toClipSpace(vec4 pos_1)\n{\n return pos_1;\n}\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n vec4 param = vec4((pos * _53.transform.xy) + _53.transform.zw, _53.z, 1.0);\n gl_Position = toClipSpace(param);\n m3x2 param_1 = m3x2(_53.uvTransformR1.xyz, _53.uvTransformR2.xyz);\n vec3 param_2 = vec3(uv, 1.0);\n vUV = transform3x2(param_1, param_2).xy;\n m3x2 param_3 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_4 = vec3(uv, 1.0);\n vec3 uv3 = transform3x2(param_3, param_4);\n vCoverUV = ((uv3 * vec3(_53.uvCoverTransform.xy, 1.0)) + vec3(_53.uvCoverTransform.zw, 0.0)).xy;\n}\n\n", - GLSL130: "#version 130\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 transform;\n vec4 uvCoverTransform;\n vec4 uvTransformR1;\n vec4 uvTransformR2;\n float z;\n};\n\nuniform Block _53;\n\nin vec2 pos;\nout vec2 vUV;\nin vec2 uv;\nout vec2 vCoverUV;\n\nvec4 toClipSpace(vec4 pos_1)\n{\n return pos_1;\n}\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n vec4 param = vec4((pos * _53.transform.xy) + _53.transform.zw, _53.z, 1.0);\n gl_Position = toClipSpace(param);\n m3x2 param_1 = m3x2(_53.uvTransformR1.xyz, _53.uvTransformR2.xyz);\n vec3 param_2 = vec3(uv, 1.0);\n vUV = transform3x2(param_1, param_2).xy;\n m3x2 param_3 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_4 = vec3(uv, 1.0);\n vec3 uv3 = transform3x2(param_3, param_4);\n vCoverUV = ((uv3 * vec3(_53.uvCoverTransform.xy, 1.0)) + vec3(_53.uvCoverTransform.zw, 0.0)).xy;\n}\n\n", - GLSL150: "#version 150\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 transform;\n vec4 uvCoverTransform;\n vec4 uvTransformR1;\n vec4 uvTransformR2;\n float z;\n};\n\nuniform Block _53;\n\nin vec2 pos;\nout vec2 vUV;\nin vec2 uv;\nout vec2 vCoverUV;\n\nvec4 toClipSpace(vec4 pos_1)\n{\n return pos_1;\n}\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n vec4 param = vec4((pos * _53.transform.xy) + _53.transform.zw, _53.z, 1.0);\n gl_Position = toClipSpace(param);\n m3x2 param_1 = m3x2(_53.uvTransformR1.xyz, _53.uvTransformR2.xyz);\n vec3 param_2 = vec3(uv, 1.0);\n vUV = transform3x2(param_1, param_2).xy;\n m3x2 param_3 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_4 = vec3(uv, 1.0);\n vec3 uv3 = transform3x2(param_3, param_4);\n vCoverUV = ((uv3 * vec3(_53.uvCoverTransform.xy, 1.0)) + vec3(_53.uvCoverTransform.zw, 0.0)).xy;\n}\n\n", - /* - struct m3x2 - { - float3 r0; - float3 r1; - }; - - static const m3x2 _115 = { float3(1.0f, 0.0f, 0.0f), float3(0.0f, -1.0f, 1.0f) }; - static const m3x2 _141 = { float3(1.0f, 0.0f, 0.0f), float3(0.0f, 1.0f, 0.0f) }; - - cbuffer Block : register(b0) - { - float4 _70_transform : packoffset(c0); - float4 _70_uvCoverTransform : packoffset(c1); - float4 _70_uvTransformR1 : packoffset(c2); - float4 _70_uvTransformR2 : packoffset(c3); - float _70_z : packoffset(c4); - }; - - - static float4 gl_Position; - static float2 pos; - static float2 vUV; - static float2 uv; - static float2 vCoverUV; - - struct SPIRV_Cross_Input - { - float2 pos : POSITION; - float2 uv : NORMAL; - }; - - struct SPIRV_Cross_Output - { - float2 vCoverUV : TEXCOORD0; - float2 vUV : TEXCOORD1; - float4 gl_Position : SV_Position; - }; - - float4 toClipSpace(float4 pos_1) - { - return float4(pos_1.xy, (pos_1.z + pos_1.w) * 0.5f, pos_1.w); - } - - float3 transform3x2(m3x2 t, float3 v) - { - return float3(dot(t.r0, v), dot(t.r1, v), dot(float3(0.0f, 0.0f, 1.0f), v)); - } - - void vert_main() - { - float4 param = float4((pos * _70_transform.xy) + _70_transform.zw, _70_z, 1.0f); - gl_Position = toClipSpace(param); - m3x2 _101 = { _70_uvTransformR1.xyz, _70_uvTransformR2.xyz }; - m3x2 param_1 = _101; - float3 param_2 = float3(uv, 1.0f); - vUV = transform3x2(param_1, param_2).xy; - m3x2 param_3 = _115; - float3 param_4 = float3(uv, 1.0f); - float3 uv3 = transform3x2(param_3, param_4); - vCoverUV = ((uv3 * float3(_70_uvCoverTransform.xy, 1.0f)) + float3(_70_uvCoverTransform.zw, 0.0f)).xy; - } - - SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) - { - pos = stage_input.pos; - uv = stage_input.uv; - vert_main(); - SPIRV_Cross_Output stage_output; - stage_output.gl_Position = gl_Position; - stage_output.vUV = vUV; - stage_output.vCoverUV = vCoverUV; - return stage_output; - } - - */ - HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0xb3, 0x71, 0x8c, 0x9f, 0x3e, 0x4f, 0x8b, 0x50, 0xb7, 0xa2, 0x4b, 0xfb, 0xad, 0x7d, 0xc8, 0x71, 0x1, 0x0, 0x0, 0x0, 0xcc, 0x5, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x78, 0x1, 0x0, 0x0, 0x1c, 0x3, 0x0, 0x0, 0x98, 0x3, 0x0, 0x0, 0xc, 0x5, 0x0, 0x0, 0x5c, 0x5, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0x38, 0x1, 0x0, 0x0, 0x38, 0x1, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0x4, 0x1, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x1, 0x0, 0x24, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x24, 0x0, 0x1, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0x51, 0x0, 0x0, 0x5, 0x6, 0x0, 0xf, 0xa0, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0xbf, 0x0, 0x0, 0x0, 0x3f, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x0, 0x80, 0x0, 0x0, 0xf, 0x90, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x1, 0x80, 0x1, 0x0, 0xf, 0x90, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x7, 0x80, 0x1, 0x0, 0xc4, 0x90, 0x6, 0x0, 0xd0, 0xa0, 0x6, 0x0, 0xc5, 0xa0, 0x8, 0x0, 0x0, 0x3, 0x0, 0x0, 0x8, 0xe0, 0x3, 0x0, 0xe4, 0xa0, 0x0, 0x0, 0xe4, 0x80, 0x8, 0x0, 0x0, 0x3, 0x0, 0x0, 0x4, 0xe0, 0x4, 0x0, 0xe4, 0xa0, 0x0, 0x0, 0xe4, 0x80, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x3, 0x80, 0x1, 0x0, 0xe1, 0x90, 0x6, 0x0, 0xe4, 0xa0, 0x6, 0x0, 0xe1, 0xa0, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 0xe4, 0x80, 0x6, 0x0, 0xe2, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0x1, 0x80, 0x1, 0x0, 0x0, 0x90, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x3, 0xe0, 0x0, 0x0, 0xe4, 0x80, 0x2, 0x0, 0xe4, 0xa0, 0x2, 0x0, 0xee, 0xa0, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 0xe4, 0x90, 0x1, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0xee, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x3, 0xc0, 0x0, 0x0, 0xe4, 0x80, 0x0, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0xb, 0x80, 0x6, 0x0, 0xe4, 0xa0, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0xc, 0xc0, 0x5, 0x0, 0x0, 0xa0, 0x0, 0x0, 0x74, 0x80, 0x0, 0x0, 0x34, 0x80, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0x9c, 0x1, 0x0, 0x0, 0x40, 0x0, 0x1, 0x0, 0x67, 0x0, 0x0, 0x0, 0x59, 0x0, 0x0, 0x4, 0x46, 0x8e, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0x32, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0xc2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x67, 0x0, 0x0, 0x4, 0xf2, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x2, 0x2, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x32, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x42, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0xf, 0x0, 0x0, 0xa, 0x22, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0xbf, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x96, 0x5, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xb, 0x32, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xe6, 0x8a, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x8, 0x42, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x82, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x46, 0x2, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x8, 0x82, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x82, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x46, 0x2, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xb, 0x32, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6, 0x8a, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xa, 0x42, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x36, 0x0, 0x0, 0x5, 0x82, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0x6c, 0x1, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xfe, 0xff, 0x0, 0x1, 0x0, 0x0, 0x44, 0x1, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x0, 0xab, 0xab, 0x3c, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xd4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf4, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0x1, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1b, 0x1, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xe4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2d, 0x1, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x34, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x37, 0x30, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x0, 0xab, 0xab, 0x1, 0x0, 0x3, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x37, 0x30, 0x5f, 0x75, 0x76, 0x43, 0x6f, 0x76, 0x65, 0x72, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x0, 0x5f, 0x37, 0x30, 0x5f, 0x75, 0x76, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x52, 0x31, 0x0, 0x5f, 0x37, 0x30, 0x5f, 0x75, 0x76, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x52, 0x32, 0x0, 0x5f, 0x37, 0x30, 0x5f, 0x7a, 0x0, 0xab, 0x0, 0x0, 0x3, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0x49, 0x53, 0x47, 0x4e, 0x48, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x0, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x0, 0x4f, 0x53, 0x47, 0x4e, 0x68, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xc, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x3, 0x0, 0x0, 0x59, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0x53, 0x56, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x0, 0xab, 0xab, 0xab}, - } - shader_intersect_frag = backend.ShaderSources{ - Textures: []backend.TextureBinding{{Name: "cover", Binding: 0}}, - GLSL100ES: "precision mediump float;\nprecision highp int;\n\nuniform mediump sampler2D cover;\n\nvarying highp vec2 vUV;\n\nvoid main()\n{\n float cover_1 = abs(texture2D(cover, vUV).x);\n gl_FragData[0].x = cover_1;\n}\n\n", - GLSL300ES: "#version 300 es\nprecision mediump float;\nprecision highp int;\n\nuniform mediump sampler2D cover;\n\nin highp vec2 vUV;\nlayout(location = 0) out vec4 fragColor;\n\nvoid main()\n{\n float cover_1 = abs(texture(cover, vUV).x);\n fragColor.x = cover_1;\n}\n\n", - GLSL130: "#version 130\n\nuniform sampler2D cover;\n\nin vec2 vUV;\nout vec4 fragColor;\n\nvoid main()\n{\n float cover_1 = abs(texture(cover, vUV).x);\n fragColor.x = cover_1;\n}\n\n", - GLSL150: "#version 150\n\nuniform sampler2D cover;\n\nin vec2 vUV;\nout vec4 fragColor;\n\nvoid main()\n{\n float cover_1 = abs(texture(cover, vUV).x);\n fragColor.x = cover_1;\n}\n\n", - /* - Texture2D<float4> cover : register(t0); - SamplerState _cover_sampler : register(s0); - - static float2 vUV; - static float4 fragColor; - - struct SPIRV_Cross_Input - { - float2 vUV : TEXCOORD0; - }; - - struct SPIRV_Cross_Output - { - float4 fragColor : SV_Target0; - }; - - void frag_main() - { - float cover_1 = abs(cover.Sample(_cover_sampler, vUV).x); - fragColor.x = cover_1; - } - - SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) - { - vUV = stage_input.vUV; - frag_main(); - SPIRV_Cross_Output stage_output; - stage_output.fragColor = fragColor; - return stage_output; - } - - */ - HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0xe0, 0xe4, 0x3, 0x8c, 0xac, 0x56, 0x46, 0x82, 0x6c, 0xe7, 0x7c, 0xc3, 0x54, 0xa6, 0x27, 0xef, 0x1, 0x0, 0x0, 0x0, 0x8, 0x3, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0xd4, 0x0, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0xfc, 0x1, 0x0, 0x0, 0xa0, 0x2, 0x0, 0x0, 0xd4, 0x2, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0x94, 0x0, 0x0, 0x0, 0x94, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x6c, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x28, 0x0, 0x1, 0x0, 0x24, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x51, 0x0, 0x0, 0x5, 0x0, 0x0, 0xf, 0xa0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x3, 0xb0, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x90, 0x0, 0x8, 0xf, 0xa0, 0x42, 0x0, 0x0, 0x3, 0x0, 0x0, 0xf, 0x80, 0x0, 0x0, 0xe4, 0xb0, 0x0, 0x8, 0xe4, 0xa0, 0x23, 0x0, 0x0, 0x2, 0x0, 0x0, 0x1, 0x80, 0x0, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0xe, 0x80, 0x0, 0x0, 0x0, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x8, 0xf, 0x80, 0x0, 0x0, 0xe4, 0x80, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0xa4, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x29, 0x0, 0x0, 0x0, 0x5a, 0x0, 0x0, 0x3, 0x0, 0x60, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x58, 0x18, 0x0, 0x4, 0x0, 0x70, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x55, 0x55, 0x0, 0x0, 0x62, 0x10, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x2, 0x1, 0x0, 0x0, 0x0, 0x45, 0x0, 0x0, 0x9, 0xf2, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x7e, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x60, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x6, 0x12, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x80, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x8, 0xe2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0x9c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0x0, 0x1, 0x0, 0x0, 0x71, 0x0, 0x0, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x6b, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xd, 0x0, 0x0, 0x0, 0x5f, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x0, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0xab, 0xab, 0xab, 0x49, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0xab, 0xab, 0xab, 0x4f, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x0, 0xab, 0xab}, - } - shader_intersect_vert = backend.ShaderSources{ - Inputs: []backend.InputLocation{{Name: "pos", Location: 0, Semantic: "POSITION", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "uv", Location: 1, Semantic: "NORMAL", SemanticIndex: 0, Type: 0x0, Size: 2}}, - Uniforms: backend.UniformsReflection{ - Blocks: []backend.UniformBlock{{Name: "Block", Binding: 0}}, - Locations: []backend.UniformLocation{{Name: "_78.uvTransform", Type: 0x0, Size: 4, Offset: 0}, {Name: "_78.subUVTransform", Type: 0x0, Size: 4, Offset: 16}}, - Size: 32, - }, - GLSL100ES: "\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 uvTransform;\n vec4 subUVTransform;\n};\n\nuniform Block _78;\n\nattribute vec2 pos;\nattribute vec2 uv;\nvarying vec2 vUV;\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0));\n vec3 param_1 = vec3(pos, 1.0);\n vec3 p = transform3x2(param, param_1);\n gl_Position = vec4(p, 1.0);\n m3x2 param_2 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_3 = vec3(uv, 1.0);\n vec3 uv3 = transform3x2(param_2, param_3);\n vUV = (uv3.xy * _78.subUVTransform.xy) + _78.subUVTransform.zw;\n m3x2 param_4 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_5 = vec3(vUV, 1.0);\n vUV = transform3x2(param_4, param_5).xy;\n vUV = (vUV * _78.uvTransform.xy) + _78.uvTransform.zw;\n}\n\n", - GLSL300ES: "#version 300 es\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nlayout(std140) uniform Block\n{\n vec4 uvTransform;\n vec4 subUVTransform;\n} _78;\n\nlayout(location = 0) in vec2 pos;\nlayout(location = 1) in vec2 uv;\nout vec2 vUV;\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0));\n vec3 param_1 = vec3(pos, 1.0);\n vec3 p = transform3x2(param, param_1);\n gl_Position = vec4(p, 1.0);\n m3x2 param_2 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_3 = vec3(uv, 1.0);\n vec3 uv3 = transform3x2(param_2, param_3);\n vUV = (uv3.xy * _78.subUVTransform.xy) + _78.subUVTransform.zw;\n m3x2 param_4 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_5 = vec3(vUV, 1.0);\n vUV = transform3x2(param_4, param_5).xy;\n vUV = (vUV * _78.uvTransform.xy) + _78.uvTransform.zw;\n}\n\n", - GLSL130: "#version 130\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 uvTransform;\n vec4 subUVTransform;\n};\n\nuniform Block _78;\n\nin vec2 pos;\nin vec2 uv;\nout vec2 vUV;\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0));\n vec3 param_1 = vec3(pos, 1.0);\n vec3 p = transform3x2(param, param_1);\n gl_Position = vec4(p, 1.0);\n m3x2 param_2 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_3 = vec3(uv, 1.0);\n vec3 uv3 = transform3x2(param_2, param_3);\n vUV = (uv3.xy * _78.subUVTransform.xy) + _78.subUVTransform.zw;\n m3x2 param_4 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_5 = vec3(vUV, 1.0);\n vUV = transform3x2(param_4, param_5).xy;\n vUV = (vUV * _78.uvTransform.xy) + _78.uvTransform.zw;\n}\n\n", - GLSL150: "#version 150\n\nstruct m3x2\n{\n vec3 r0;\n vec3 r1;\n};\n\nstruct Block\n{\n vec4 uvTransform;\n vec4 subUVTransform;\n};\n\nuniform Block _78;\n\nin vec2 pos;\nin vec2 uv;\nout vec2 vUV;\n\nvec3 transform3x2(m3x2 t, vec3 v)\n{\n return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v));\n}\n\nvoid main()\n{\n m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0));\n vec3 param_1 = vec3(pos, 1.0);\n vec3 p = transform3x2(param, param_1);\n gl_Position = vec4(p, 1.0);\n m3x2 param_2 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_3 = vec3(uv, 1.0);\n vec3 uv3 = transform3x2(param_2, param_3);\n vUV = (uv3.xy * _78.subUVTransform.xy) + _78.subUVTransform.zw;\n m3x2 param_4 = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));\n vec3 param_5 = vec3(vUV, 1.0);\n vUV = transform3x2(param_4, param_5).xy;\n vUV = (vUV * _78.uvTransform.xy) + _78.uvTransform.zw;\n}\n\n", - /* - struct m3x2 - { - float3 r0; - float3 r1; - }; - - static const m3x2 _38 = { float3(1.0f, 0.0f, 0.0f), float3(0.0f, 1.0f, 0.0f) }; - static const m3x2 _63 = { float3(1.0f, 0.0f, 0.0f), float3(0.0f, -1.0f, 1.0f) }; - - cbuffer Block : register(b0) - { - float4 _78_uvTransform : packoffset(c0); - float4 _78_subUVTransform : packoffset(c1); - }; - - - static float4 gl_Position; - static float2 pos; - static float2 uv; - static float2 vUV; - - struct SPIRV_Cross_Input - { - float2 pos : POSITION; - float2 uv : NORMAL; - }; - - struct SPIRV_Cross_Output - { - float2 vUV : TEXCOORD0; - float4 gl_Position : SV_Position; - }; - - float3 transform3x2(m3x2 t, float3 v) - { - return float3(dot(t.r0, v), dot(t.r1, v), dot(float3(0.0f, 0.0f, 1.0f), v)); - } - - void vert_main() - { - m3x2 param = _38; - float3 param_1 = float3(pos, 1.0f); - float3 p = transform3x2(param, param_1); - gl_Position = float4(p, 1.0f); - m3x2 param_2 = _63; - float3 param_3 = float3(uv, 1.0f); - float3 uv3 = transform3x2(param_2, param_3); - vUV = (uv3.xy * _78_subUVTransform.xy) + _78_subUVTransform.zw; - m3x2 param_4 = _63; - float3 param_5 = float3(vUV, 1.0f); - vUV = transform3x2(param_4, param_5).xy; - vUV = (vUV * _78_uvTransform.xy) + _78_uvTransform.zw; - } - - SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) - { - pos = stage_input.pos; - uv = stage_input.uv; - vert_main(); - SPIRV_Cross_Output stage_output; - stage_output.gl_Position = gl_Position; - stage_output.vUV = vUV; - return stage_output; - } - - */ - HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0x27, 0x8, 0x9f, 0xbf, 0x30, 0xa, 0x5b, 0x38, 0xe, 0x78, 0x0, 0x22, 0xdb, 0x3b, 0x30, 0x54, 0x1, 0x0, 0x0, 0x0, 0xd8, 0x4, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x4c, 0x1, 0x0, 0x0, 0xc4, 0x2, 0x0, 0x0, 0x40, 0x3, 0x0, 0x0, 0x30, 0x4, 0x0, 0x0, 0x80, 0x4, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0xc, 0x1, 0x0, 0x0, 0xc, 0x1, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0xd8, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x1, 0x0, 0x24, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x24, 0x0, 0x1, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0x51, 0x0, 0x0, 0x5, 0x3, 0x0, 0xf, 0xa0, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0xbf, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x0, 0x80, 0x0, 0x0, 0xf, 0x90, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x1, 0x80, 0x1, 0x0, 0xf, 0x90, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x3, 0x80, 0x1, 0x0, 0x55, 0x90, 0x3, 0x0, 0xe4, 0xa0, 0x3, 0x0, 0xe1, 0xa0, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 0xe4, 0x80, 0x3, 0x0, 0xe2, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0x1, 0x80, 0x1, 0x0, 0x0, 0x90, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 0xe4, 0x80, 0x2, 0x0, 0xe4, 0xa0, 0x2, 0x0, 0xee, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0x4, 0x80, 0x3, 0x0, 0x0, 0xa0, 0x8, 0x0, 0x0, 0x3, 0x0, 0x0, 0x8, 0x80, 0x3, 0x0, 0xc9, 0xa0, 0x0, 0x0, 0xe4, 0x80, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x3, 0xe0, 0x0, 0x0, 0xec, 0x80, 0x1, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0xee, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x3, 0xc0, 0x0, 0x0, 0xe4, 0x90, 0x0, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0xc, 0xc0, 0x3, 0x0, 0x0, 0xa0, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0x70, 0x1, 0x0, 0x0, 0x40, 0x0, 0x1, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x59, 0x0, 0x0, 0x4, 0x46, 0x8e, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0x32, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x67, 0x0, 0x0, 0x4, 0xf2, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x2, 0x1, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x22, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x36, 0x0, 0x0, 0x5, 0x52, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x56, 0x14, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0xa, 0x82, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0xbf, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xb, 0x32, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6, 0xa, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xe6, 0x8a, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x42, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0xf, 0x0, 0x0, 0xa, 0x82, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0xbf, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x96, 0x5, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xb, 0x32, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc6, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6, 0x8a, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x32, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x8, 0xc2, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x80, 0x3f, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0xe8, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xfe, 0xff, 0x0, 0x1, 0x0, 0x0, 0xbf, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x0, 0xab, 0xab, 0x3c, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x9c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x9c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x37, 0x38, 0x5f, 0x75, 0x76, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x0, 0x1, 0x0, 0x3, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x37, 0x38, 0x5f, 0x73, 0x75, 0x62, 0x55, 0x56, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0xab, 0x49, 0x53, 0x47, 0x4e, 0x48, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x0, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x0, 0x4f, 0x53, 0x47, 0x4e, 0x50, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xc, 0x0, 0x0, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0x53, 0x56, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x0, 0xab, 0xab, 0xab}, - } - shader_stencil_frag = backend.ShaderSources{ - GLSL100ES: "precision mediump float;\nprecision highp int;\n\nvarying vec2 vTo;\nvarying vec2 vFrom;\nvarying vec2 vCtrl;\n\nvoid main()\n{\n float dx = vTo.x - vFrom.x;\n bool increasing = vTo.x >= vFrom.x;\n bvec2 _35 = bvec2(increasing);\n vec2 left = vec2(_35.x ? vFrom.x : vTo.x, _35.y ? vFrom.y : vTo.y);\n bvec2 _41 = bvec2(increasing);\n vec2 right = vec2(_41.x ? vTo.x : vFrom.x, _41.y ? vTo.y : vFrom.y);\n vec2 extent = clamp(vec2(vFrom.x, vTo.x), vec2(-0.5), vec2(0.5));\n float midx = mix(extent.x, extent.y, 0.5);\n float x0 = midx - left.x;\n vec2 p1 = vCtrl - left;\n vec2 v = right - vCtrl;\n float t = x0 / (p1.x + sqrt((p1.x * p1.x) + ((v.x - p1.x) * x0)));\n float y = mix(mix(left.y, vCtrl.y, t), mix(vCtrl.y, right.y, t), t);\n vec2 d_half = mix(p1, v, vec2(t));\n float dy = d_half.y / d_half.x;\n float width = extent.y - extent.x;\n dy = abs(dy * width);\n vec4 sides = vec4((dy * 0.5) + y, (dy * (-0.5)) + y, (0.5 - y) / dy, ((-0.5) - y) / dy);\n sides = clamp(sides + vec4(0.5), vec4(0.0), vec4(1.0));\n float area = 0.5 * ((((sides.z - (sides.z * sides.y)) + 1.0) - sides.x) + (sides.x * sides.w));\n area *= width;\n if (width == 0.0)\n {\n area = 0.0;\n }\n gl_FragData[0].x = area;\n}\n\n", - GLSL300ES: "#version 300 es\nprecision mediump float;\nprecision highp int;\n\nin vec2 vTo;\nin vec2 vFrom;\nin vec2 vCtrl;\nlayout(location = 0) out vec4 fragCover;\n\nvoid main()\n{\n float dx = vTo.x - vFrom.x;\n bool increasing = vTo.x >= vFrom.x;\n bvec2 _35 = bvec2(increasing);\n vec2 left = vec2(_35.x ? vFrom.x : vTo.x, _35.y ? vFrom.y : vTo.y);\n bvec2 _41 = bvec2(increasing);\n vec2 right = vec2(_41.x ? vTo.x : vFrom.x, _41.y ? vTo.y : vFrom.y);\n vec2 extent = clamp(vec2(vFrom.x, vTo.x), vec2(-0.5), vec2(0.5));\n float midx = mix(extent.x, extent.y, 0.5);\n float x0 = midx - left.x;\n vec2 p1 = vCtrl - left;\n vec2 v = right - vCtrl;\n float t = x0 / (p1.x + sqrt((p1.x * p1.x) + ((v.x - p1.x) * x0)));\n float y = mix(mix(left.y, vCtrl.y, t), mix(vCtrl.y, right.y, t), t);\n vec2 d_half = mix(p1, v, vec2(t));\n float dy = d_half.y / d_half.x;\n float width = extent.y - extent.x;\n dy = abs(dy * width);\n vec4 sides = vec4((dy * 0.5) + y, (dy * (-0.5)) + y, (0.5 - y) / dy, ((-0.5) - y) / dy);\n sides = clamp(sides + vec4(0.5), vec4(0.0), vec4(1.0));\n float area = 0.5 * ((((sides.z - (sides.z * sides.y)) + 1.0) - sides.x) + (sides.x * sides.w));\n area *= width;\n if (width == 0.0)\n {\n area = 0.0;\n }\n fragCover.x = area;\n}\n\n", - GLSL130: "#version 130\n\nin vec2 vTo;\nin vec2 vFrom;\nin vec2 vCtrl;\nout vec4 fragCover;\n\nvoid main()\n{\n float dx = vTo.x - vFrom.x;\n bool increasing = vTo.x >= vFrom.x;\n bvec2 _35 = bvec2(increasing);\n vec2 left = vec2(_35.x ? vFrom.x : vTo.x, _35.y ? vFrom.y : vTo.y);\n bvec2 _41 = bvec2(increasing);\n vec2 right = vec2(_41.x ? vTo.x : vFrom.x, _41.y ? vTo.y : vFrom.y);\n vec2 extent = clamp(vec2(vFrom.x, vTo.x), vec2(-0.5), vec2(0.5));\n float midx = mix(extent.x, extent.y, 0.5);\n float x0 = midx - left.x;\n vec2 p1 = vCtrl - left;\n vec2 v = right - vCtrl;\n float t = x0 / (p1.x + sqrt((p1.x * p1.x) + ((v.x - p1.x) * x0)));\n float y = mix(mix(left.y, vCtrl.y, t), mix(vCtrl.y, right.y, t), t);\n vec2 d_half = mix(p1, v, vec2(t));\n float dy = d_half.y / d_half.x;\n float width = extent.y - extent.x;\n dy = abs(dy * width);\n vec4 sides = vec4((dy * 0.5) + y, (dy * (-0.5)) + y, (0.5 - y) / dy, ((-0.5) - y) / dy);\n sides = clamp(sides + vec4(0.5), vec4(0.0), vec4(1.0));\n float area = 0.5 * ((((sides.z - (sides.z * sides.y)) + 1.0) - sides.x) + (sides.x * sides.w));\n area *= width;\n if (width == 0.0)\n {\n area = 0.0;\n }\n fragCover.x = area;\n}\n\n", - GLSL150: "#version 150\n\nin vec2 vTo;\nin vec2 vFrom;\nin vec2 vCtrl;\nout vec4 fragCover;\n\nvoid main()\n{\n float dx = vTo.x - vFrom.x;\n bool increasing = vTo.x >= vFrom.x;\n bvec2 _35 = bvec2(increasing);\n vec2 left = vec2(_35.x ? vFrom.x : vTo.x, _35.y ? vFrom.y : vTo.y);\n bvec2 _41 = bvec2(increasing);\n vec2 right = vec2(_41.x ? vTo.x : vFrom.x, _41.y ? vTo.y : vFrom.y);\n vec2 extent = clamp(vec2(vFrom.x, vTo.x), vec2(-0.5), vec2(0.5));\n float midx = mix(extent.x, extent.y, 0.5);\n float x0 = midx - left.x;\n vec2 p1 = vCtrl - left;\n vec2 v = right - vCtrl;\n float t = x0 / (p1.x + sqrt((p1.x * p1.x) + ((v.x - p1.x) * x0)));\n float y = mix(mix(left.y, vCtrl.y, t), mix(vCtrl.y, right.y, t), t);\n vec2 d_half = mix(p1, v, vec2(t));\n float dy = d_half.y / d_half.x;\n float width = extent.y - extent.x;\n dy = abs(dy * width);\n vec4 sides = vec4((dy * 0.5) + y, (dy * (-0.5)) + y, (0.5 - y) / dy, ((-0.5) - y) / dy);\n sides = clamp(sides + vec4(0.5), vec4(0.0), vec4(1.0));\n float area = 0.5 * ((((sides.z - (sides.z * sides.y)) + 1.0) - sides.x) + (sides.x * sides.w));\n area *= width;\n if (width == 0.0)\n {\n area = 0.0;\n }\n fragCover.x = area;\n}\n\n", - /* - static float2 vTo; - static float2 vFrom; - static float2 vCtrl; - static float4 fragCover; - - struct SPIRV_Cross_Input - { - float2 vFrom : TEXCOORD0; - float2 vCtrl : TEXCOORD1; - float2 vTo : TEXCOORD2; - }; - - struct SPIRV_Cross_Output - { - float4 fragCover : SV_Target0; - }; - - void frag_main() - { - float dx = vTo.x - vFrom.x; - bool increasing = vTo.x >= vFrom.x; - bool2 _35 = increasing.xx; - float2 left = float2(_35.x ? vFrom.x : vTo.x, _35.y ? vFrom.y : vTo.y); - bool2 _41 = increasing.xx; - float2 right = float2(_41.x ? vTo.x : vFrom.x, _41.y ? vTo.y : vFrom.y); - float2 extent = clamp(float2(vFrom.x, vTo.x), (-0.5f).xx, 0.5f.xx); - float midx = lerp(extent.x, extent.y, 0.5f); - float x0 = midx - left.x; - float2 p1 = vCtrl - left; - float2 v = right - vCtrl; - float t = x0 / (p1.x + sqrt((p1.x * p1.x) + ((v.x - p1.x) * x0))); - float y = lerp(lerp(left.y, vCtrl.y, t), lerp(vCtrl.y, right.y, t), t); - float2 d_half = lerp(p1, v, t.xx); - float dy = d_half.y / d_half.x; - float width = extent.y - extent.x; - dy = abs(dy * width); - float4 sides = float4((dy * 0.5f) + y, (dy * (-0.5f)) + y, (0.5f - y) / dy, ((-0.5f) - y) / dy); - sides = clamp(sides + 0.5f.xxxx, 0.0f.xxxx, 1.0f.xxxx); - float area = 0.5f * ((((sides.z - (sides.z * sides.y)) + 1.0f) - sides.x) + (sides.x * sides.w)); - area *= width; - if (width == 0.0f) - { - area = 0.0f; - } - fragCover.x = area; - } - - SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) - { - vTo = stage_input.vTo; - vFrom = stage_input.vFrom; - vCtrl = stage_input.vCtrl; - frag_main(); - SPIRV_Cross_Output stage_output; - stage_output.fragCover = fragCover; - return stage_output; - } - - */ - HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0x94, 0x21, 0xb9, 0x13, 0x4c, 0xba, 0xd, 0x11, 0x8f, 0xc7, 0xce, 0xe, 0x41, 0x73, 0xec, 0xe1, 0x1, 0x0, 0x0, 0x0, 0x5c, 0xa, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x9c, 0x3, 0x0, 0x0, 0xfc, 0x8, 0x0, 0x0, 0x78, 0x9, 0x0, 0x0, 0xc4, 0x9, 0x0, 0x0, 0x28, 0xa, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0x5c, 0x3, 0x0, 0x0, 0x5c, 0x3, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x38, 0x3, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x2, 0xff, 0xff, 0x51, 0x0, 0x0, 0x5, 0x0, 0x0, 0xf, 0xa0, 0x0, 0x0, 0x0, 0xbf, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0xf, 0xb0, 0x1f, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x80, 0x1, 0x0, 0x3, 0xb0, 0xb, 0x0, 0x0, 0x3, 0x0, 0x0, 0x1, 0x80, 0x0, 0x0, 0x0, 0xb0, 0x0, 0x0, 0x0, 0xa0, 0xb, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x1, 0x0, 0x0, 0xb0, 0x0, 0x0, 0x0, 0xa0, 0xa, 0x0, 0x0, 0x3, 0x1, 0x0, 0x3, 0x80, 0x0, 0x0, 0xe4, 0x80, 0x0, 0x0, 0x55, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x1, 0x80, 0x1, 0x0, 0x0, 0x81, 0x1, 0x0, 0x55, 0x80, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x55, 0xa0, 0x1, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0x2, 0x1, 0x0, 0x3, 0x80, 0x0, 0x0, 0xe4, 0xb0, 0xa, 0x0, 0x0, 0x3, 0x2, 0x0, 0x1, 0x80, 0x1, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0xb0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x2, 0x0, 0x0, 0x81, 0xb, 0x0, 0x0, 0x3, 0x3, 0x0, 0x1, 0x80, 0x1, 0x0, 0x0, 0xb0, 0x1, 0x0, 0x0, 0x80, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x4, 0x80, 0x1, 0x0, 0x0, 0x81, 0x1, 0x0, 0x0, 0xb0, 0x58, 0x0, 0x0, 0x4, 0x3, 0x0, 0x2, 0x80, 0x0, 0x0, 0xaa, 0x80, 0x1, 0x0, 0x55, 0xb0, 0x1, 0x0, 0x55, 0x80, 0x58, 0x0, 0x0, 0x4, 0x2, 0x0, 0x2, 0x80, 0x0, 0x0, 0xaa, 0x80, 0x1, 0x0, 0x55, 0x80, 0x1, 0x0, 0x55, 0xb0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0xc, 0x80, 0x3, 0x0, 0x1b, 0x80, 0x0, 0x0, 0xe4, 0xb1, 0x2, 0x0, 0x0, 0x3, 0x1, 0x0, 0x3, 0x80, 0x2, 0x0, 0xe4, 0x81, 0x0, 0x0, 0x1b, 0xb0, 0x2, 0x0, 0x0, 0x3, 0x1, 0x0, 0x4, 0x80, 0x0, 0x0, 0xff, 0x80, 0x1, 0x0, 0x0, 0x81, 0x5, 0x0, 0x0, 0x3, 0x1, 0x0, 0x4, 0x80, 0x0, 0x0, 0x55, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x4, 0x0, 0x0, 0x4, 0x1, 0x0, 0x4, 0x80, 0x1, 0x0, 0x0, 0x80, 0x1, 0x0, 0x0, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x7, 0x0, 0x0, 0x2, 0x1, 0x0, 0x4, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x6, 0x0, 0x0, 0x2, 0x1, 0x0, 0x4, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x2, 0x0, 0x0, 0x3, 0x1, 0x0, 0x4, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x1, 0x0, 0x0, 0x80, 0x6, 0x0, 0x0, 0x2, 0x1, 0x0, 0x4, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x4, 0x0, 0x0, 0x4, 0x1, 0x0, 0x4, 0x80, 0x0, 0x0, 0x55, 0x80, 0x1, 0x0, 0x55, 0x80, 0x2, 0x0, 0x55, 0x80, 0x12, 0x0, 0x0, 0x4, 0x2, 0x0, 0x3, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0x1b, 0x80, 0x1, 0x0, 0xe4, 0x80, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x4, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0xaa, 0x80, 0x0, 0x0, 0xaa, 0xb0, 0x12, 0x0, 0x0, 0x4, 0x2, 0x0, 0x4, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0xaa, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x6, 0x0, 0x0, 0x2, 0x0, 0x0, 0x2, 0x80, 0x2, 0x0, 0x0, 0x80, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x2, 0x0, 0x55, 0x80, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x55, 0x80, 0x23, 0x0, 0x0, 0x2, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x4, 0x0, 0x0, 0x4, 0x1, 0x0, 0x1, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0x55, 0xa0, 0x2, 0x0, 0xaa, 0x80, 0x4, 0x0, 0x0, 0x4, 0x1, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0x0, 0xa0, 0x2, 0x0, 0xaa, 0x80, 0x6, 0x0, 0x0, 0x2, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0xc, 0x80, 0x2, 0x0, 0xaa, 0x81, 0x0, 0x0, 0x1b, 0xa0, 0x5, 0x0, 0x0, 0x3, 0x1, 0x0, 0x8, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0xff, 0x80, 0x5, 0x0, 0x0, 0x3, 0x1, 0x0, 0x4, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0xaa, 0x80, 0x2, 0x0, 0x0, 0x3, 0x1, 0x0, 0x1f, 0x80, 0x1, 0x0, 0xe4, 0x80, 0x0, 0x0, 0x55, 0xa0, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x2, 0x80, 0x1, 0x0, 0xaa, 0x80, 0x1, 0x0, 0x55, 0x81, 0x1, 0x0, 0xaa, 0x80, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0xaa, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x1, 0x0, 0x0, 0x81, 0x0, 0x0, 0x55, 0x80, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x2, 0x80, 0x1, 0x0, 0x0, 0x80, 0x1, 0x0, 0xff, 0x80, 0x0, 0x0, 0x55, 0x80, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0x0, 0x80, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0x1, 0x80, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x80, 0x5, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x0, 0x0, 0x55, 0xa0, 0x58, 0x0, 0x0, 0x4, 0x0, 0x0, 0x1, 0x80, 0x0, 0x0, 0x0, 0x81, 0x0, 0x0, 0xff, 0xa0, 0x0, 0x0, 0x55, 0x80, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0xe, 0x80, 0x0, 0x0, 0xff, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x8, 0xf, 0x80, 0x0, 0x0, 0xe4, 0x80, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0x58, 0x5, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0x56, 0x1, 0x0, 0x0, 0x62, 0x10, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x62, 0x10, 0x0, 0x3, 0xc2, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x62, 0x10, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x2, 0x3, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0x22, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0xa, 0x32, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbf, 0x0, 0x0, 0x0, 0xbf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x33, 0x0, 0x0, 0xa, 0x32, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x22, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0x9, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x33, 0x0, 0x0, 0x7, 0x32, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x6, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x7, 0x32, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x6, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1d, 0x0, 0x0, 0x7, 0x42, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x37, 0x0, 0x0, 0x9, 0x42, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1a, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x37, 0x0, 0x0, 0x9, 0x42, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x72, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x46, 0x2, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0xa6, 0x1b, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xc2, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x56, 0x9, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa6, 0x1e, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xb2, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa6, 0xe, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x8, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x7, 0x12, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0x9, 0x12, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x4b, 0x0, 0x0, 0x5, 0x12, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x12, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x7, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0x9, 0x12, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0x9, 0xc2, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x56, 0xd, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa6, 0xe, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x7, 0x42, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x7, 0x42, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0x9, 0x82, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x3a, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x82, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0x9, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0x32, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x6, 0x0, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0xbf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xd, 0x32, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0xa6, 0xa, 0x10, 0x80, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0xbf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x8, 0xc2, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x6, 0x4, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa6, 0xa, 0x10, 0x80, 0x81, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0xa, 0xf2, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x46, 0xe, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x3f, 0x32, 0x0, 0x0, 0xa, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x0, 0x8, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0x9, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x7, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x18, 0x0, 0x0, 0x7, 0x22, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x7, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x37, 0x0, 0x0, 0x9, 0x12, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x8, 0xe2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x29, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0x44, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0x0, 0x1, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0x49, 0x53, 0x47, 0x4e, 0x5c, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0xc, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0xab, 0xab, 0xab, 0x4f, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x0, 0xab, 0xab}, - } - shader_stencil_vert = backend.ShaderSources{ - Inputs: []backend.InputLocation{{Name: "corner", Location: 0, Semantic: "POSITION", SemanticIndex: 0, Type: 0x0, Size: 1}, {Name: "maxy", Location: 1, Semantic: "NORMAL", SemanticIndex: 0, Type: 0x0, Size: 1}, {Name: "from", Location: 2, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "ctrl", Location: 3, Semantic: "TEXCOORD", SemanticIndex: 1, Type: 0x0, Size: 2}, {Name: "to", Location: 4, Semantic: "TEXCOORD", SemanticIndex: 2, Type: 0x0, Size: 2}}, - Uniforms: backend.UniformsReflection{ - Blocks: []backend.UniformBlock{{Name: "Block", Binding: 0}}, - Locations: []backend.UniformLocation{{Name: "_16.transform", Type: 0x0, Size: 4, Offset: 0}, {Name: "_16.pathOffset", Type: 0x0, Size: 2, Offset: 16}}, - Size: 24, - }, - GLSL100ES: "\nstruct Block\n{\n vec4 transform;\n vec2 pathOffset;\n};\n\nuniform Block _16;\n\nattribute vec2 from;\nattribute vec2 ctrl;\nattribute vec2 to;\nattribute float maxy;\nattribute float corner;\nvarying vec2 vFrom;\nvarying vec2 vCtrl;\nvarying vec2 vTo;\n\nvoid main()\n{\n vec2 from_1 = from + _16.pathOffset;\n vec2 ctrl_1 = ctrl + _16.pathOffset;\n vec2 to_1 = to + _16.pathOffset;\n float maxy_1 = maxy + _16.pathOffset.y;\n float c = corner;\n vec2 pos;\n if (c >= 0.375)\n {\n c -= 0.5;\n pos.y = maxy_1 + 1.0;\n }\n else\n {\n pos.y = min(min(from_1.y, ctrl_1.y), to_1.y) - 1.0;\n }\n if (c >= 0.125)\n {\n pos.x = max(max(from_1.x, ctrl_1.x), to_1.x) + 1.0;\n }\n else\n {\n pos.x = min(min(from_1.x, ctrl_1.x), to_1.x) - 1.0;\n }\n vFrom = from_1 - pos;\n vCtrl = ctrl_1 - pos;\n vTo = to_1 - pos;\n pos = (pos * _16.transform.xy) + _16.transform.zw;\n gl_Position = vec4(pos, 1.0, 1.0);\n}\n\n", - GLSL300ES: "#version 300 es\n\nlayout(std140) uniform Block\n{\n vec4 transform;\n vec2 pathOffset;\n} _16;\n\nlayout(location = 2) in vec2 from;\nlayout(location = 3) in vec2 ctrl;\nlayout(location = 4) in vec2 to;\nlayout(location = 1) in float maxy;\nlayout(location = 0) in float corner;\nout vec2 vFrom;\nout vec2 vCtrl;\nout vec2 vTo;\n\nvoid main()\n{\n vec2 from_1 = from + _16.pathOffset;\n vec2 ctrl_1 = ctrl + _16.pathOffset;\n vec2 to_1 = to + _16.pathOffset;\n float maxy_1 = maxy + _16.pathOffset.y;\n float c = corner;\n vec2 pos;\n if (c >= 0.375)\n {\n c -= 0.5;\n pos.y = maxy_1 + 1.0;\n }\n else\n {\n pos.y = min(min(from_1.y, ctrl_1.y), to_1.y) - 1.0;\n }\n if (c >= 0.125)\n {\n pos.x = max(max(from_1.x, ctrl_1.x), to_1.x) + 1.0;\n }\n else\n {\n pos.x = min(min(from_1.x, ctrl_1.x), to_1.x) - 1.0;\n }\n vFrom = from_1 - pos;\n vCtrl = ctrl_1 - pos;\n vTo = to_1 - pos;\n pos = (pos * _16.transform.xy) + _16.transform.zw;\n gl_Position = vec4(pos, 1.0, 1.0);\n}\n\n", - GLSL130: "#version 130\n\nstruct Block\n{\n vec4 transform;\n vec2 pathOffset;\n};\n\nuniform Block _16;\n\nin vec2 from;\nin vec2 ctrl;\nin vec2 to;\nin float maxy;\nin float corner;\nout vec2 vFrom;\nout vec2 vCtrl;\nout vec2 vTo;\n\nvoid main()\n{\n vec2 from_1 = from + _16.pathOffset;\n vec2 ctrl_1 = ctrl + _16.pathOffset;\n vec2 to_1 = to + _16.pathOffset;\n float maxy_1 = maxy + _16.pathOffset.y;\n float c = corner;\n vec2 pos;\n if (c >= 0.375)\n {\n c -= 0.5;\n pos.y = maxy_1 + 1.0;\n }\n else\n {\n pos.y = min(min(from_1.y, ctrl_1.y), to_1.y) - 1.0;\n }\n if (c >= 0.125)\n {\n pos.x = max(max(from_1.x, ctrl_1.x), to_1.x) + 1.0;\n }\n else\n {\n pos.x = min(min(from_1.x, ctrl_1.x), to_1.x) - 1.0;\n }\n vFrom = from_1 - pos;\n vCtrl = ctrl_1 - pos;\n vTo = to_1 - pos;\n pos = (pos * _16.transform.xy) + _16.transform.zw;\n gl_Position = vec4(pos, 1.0, 1.0);\n}\n\n", - GLSL150: "#version 150\n\nstruct Block\n{\n vec4 transform;\n vec2 pathOffset;\n};\n\nuniform Block _16;\n\nin vec2 from;\nin vec2 ctrl;\nin vec2 to;\nin float maxy;\nin float corner;\nout vec2 vFrom;\nout vec2 vCtrl;\nout vec2 vTo;\n\nvoid main()\n{\n vec2 from_1 = from + _16.pathOffset;\n vec2 ctrl_1 = ctrl + _16.pathOffset;\n vec2 to_1 = to + _16.pathOffset;\n float maxy_1 = maxy + _16.pathOffset.y;\n float c = corner;\n vec2 pos;\n if (c >= 0.375)\n {\n c -= 0.5;\n pos.y = maxy_1 + 1.0;\n }\n else\n {\n pos.y = min(min(from_1.y, ctrl_1.y), to_1.y) - 1.0;\n }\n if (c >= 0.125)\n {\n pos.x = max(max(from_1.x, ctrl_1.x), to_1.x) + 1.0;\n }\n else\n {\n pos.x = min(min(from_1.x, ctrl_1.x), to_1.x) - 1.0;\n }\n vFrom = from_1 - pos;\n vCtrl = ctrl_1 - pos;\n vTo = to_1 - pos;\n pos = (pos * _16.transform.xy) + _16.transform.zw;\n gl_Position = vec4(pos, 1.0, 1.0);\n}\n\n", - /* - cbuffer Block : register(b0) - { - float4 _16_transform : packoffset(c0); - float2 _16_pathOffset : packoffset(c1); - }; - - - static float4 gl_Position; - static float2 from; - static float2 ctrl; - static float2 to; - static float maxy; - static float corner; - static float2 vFrom; - static float2 vCtrl; - static float2 vTo; - - struct SPIRV_Cross_Input - { - float corner : POSITION; - float maxy : NORMAL; - float2 from : TEXCOORD0; - float2 ctrl : TEXCOORD1; - float2 to : TEXCOORD2; - }; - - struct SPIRV_Cross_Output - { - float2 vFrom : TEXCOORD0; - float2 vCtrl : TEXCOORD1; - float2 vTo : TEXCOORD2; - float4 gl_Position : SV_Position; - }; - - void vert_main() - { - float2 from_1 = from + _16_pathOffset; - float2 ctrl_1 = ctrl + _16_pathOffset; - float2 to_1 = to + _16_pathOffset; - float maxy_1 = maxy + _16_pathOffset.y; - float c = corner; - float2 pos; - if (c >= 0.375f) - { - c -= 0.5f; - pos.y = maxy_1 + 1.0f; - } - else - { - pos.y = min(min(from_1.y, ctrl_1.y), to_1.y) - 1.0f; - } - if (c >= 0.125f) - { - pos.x = max(max(from_1.x, ctrl_1.x), to_1.x) + 1.0f; - } - else - { - pos.x = min(min(from_1.x, ctrl_1.x), to_1.x) - 1.0f; - } - vFrom = from_1 - pos; - vCtrl = ctrl_1 - pos; - vTo = to_1 - pos; - pos = (pos * _16_transform.xy) + _16_transform.zw; - gl_Position = float4(pos, 1.0f, 1.0f); - } - - SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) - { - from = stage_input.from; - ctrl = stage_input.ctrl; - to = stage_input.to; - maxy = stage_input.maxy; - corner = stage_input.corner; - vert_main(); - SPIRV_Cross_Output stage_output; - stage_output.gl_Position = gl_Position; - stage_output.vFrom = vFrom; - stage_output.vCtrl = vCtrl; - stage_output.vTo = vTo; - return stage_output; - } - - */ - HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0x99, 0xea, 0x97, 0xb5, 0xa8, 0xd5, 0x84, 0x5e, 0x4b, 0x12, 0x14, 0x56, 0xdd, 0xee, 0xfc, 0x44, 0x1, 0x0, 0x0, 0x0, 0x18, 0x8, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x4c, 0x2, 0x0, 0x0, 0x74, 0x5, 0x0, 0x0, 0xf0, 0x5, 0x0, 0x0, 0xec, 0x6, 0x0, 0x0, 0x90, 0x7, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0xc, 0x2, 0x0, 0x0, 0xc, 0x2, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0xd8, 0x1, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x1, 0x0, 0x24, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x24, 0x0, 0x1, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0x51, 0x0, 0x0, 0x5, 0x3, 0x0, 0xf, 0xa0, 0x0, 0x0, 0xc0, 0x3e, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x80, 0xbf, 0x0, 0x0, 0x0, 0xbf, 0x51, 0x0, 0x0, 0x5, 0x4, 0x0, 0xf, 0xa0, 0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x0, 0x80, 0x0, 0x0, 0xf, 0x90, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x1, 0x80, 0x1, 0x0, 0xf, 0x90, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x2, 0x80, 0x2, 0x0, 0xf, 0x90, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x3, 0x80, 0x3, 0x0, 0xf, 0x90, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x4, 0x80, 0x4, 0x0, 0xf, 0x90, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x1, 0x80, 0x1, 0x0, 0x0, 0x90, 0x2, 0x0, 0x55, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x4, 0x80, 0x0, 0x0, 0x0, 0x80, 0x3, 0x0, 0x55, 0xa0, 0xd, 0x0, 0x0, 0x3, 0x0, 0x0, 0x1, 0x80, 0x0, 0x0, 0x0, 0x90, 0x3, 0x0, 0x0, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x1, 0x0, 0x4, 0x80, 0x0, 0x0, 0x0, 0x90, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x0, 0x90, 0x3, 0x0, 0xff, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x2, 0x0, 0x3, 0x80, 0x2, 0x0, 0xe4, 0x90, 0x2, 0x0, 0xe4, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x2, 0x0, 0xc, 0x80, 0x3, 0x0, 0x14, 0x90, 0x2, 0x0, 0x14, 0xa0, 0xa, 0x0, 0x0, 0x3, 0x3, 0x0, 0x3, 0x80, 0x2, 0x0, 0xee, 0x80, 0x2, 0x0, 0xe1, 0x80, 0x2, 0x0, 0x0, 0x3, 0x3, 0x0, 0xc, 0x80, 0x4, 0x0, 0x44, 0x90, 0x2, 0x0, 0x44, 0xa0, 0xa, 0x0, 0x0, 0x3, 0x3, 0x0, 0x3, 0x80, 0x3, 0x0, 0xeb, 0x80, 0x3, 0x0, 0xe4, 0x80, 0x2, 0x0, 0x0, 0x3, 0x1, 0x0, 0x3, 0x80, 0x3, 0x0, 0xe4, 0x80, 0x3, 0x0, 0xaa, 0xa0, 0x12, 0x0, 0x0, 0x4, 0x4, 0x0, 0x6, 0x80, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0xe4, 0x80, 0x1, 0x0, 0xc8, 0x80, 0xd, 0x0, 0x0, 0x3, 0x0, 0x0, 0x1, 0x80, 0x4, 0x0, 0x55, 0x80, 0x4, 0x0, 0x0, 0xa0, 0xb, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x2, 0x0, 0xff, 0x80, 0x2, 0x0, 0x0, 0x80, 0xb, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x3, 0x0, 0xaa, 0x80, 0x0, 0x0, 0x55, 0x80, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x80, 0x0, 0x0, 0x55, 0x80, 0x3, 0x0, 0x55, 0xa0, 0x12, 0x0, 0x0, 0x4, 0x4, 0x0, 0x1, 0x80, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x55, 0x80, 0x1, 0x0, 0x55, 0x80, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0xf, 0xe0, 0x2, 0x0, 0xe4, 0x80, 0x4, 0x0, 0x28, 0x81, 0x2, 0x0, 0x0, 0x3, 0x1, 0x0, 0x3, 0xe0, 0x3, 0x0, 0xee, 0x80, 0x4, 0x0, 0xe8, 0x81, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x3, 0x80, 0x4, 0x0, 0xe8, 0x80, 0x1, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0xee, 0xa0, 0x2, 0x0, 0x0, 0x3, 0x0, 0x0, 0x3, 0xc0, 0x0, 0x0, 0xe4, 0x80, 0x0, 0x0, 0xe4, 0xa0, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0xc, 0xc0, 0x3, 0x0, 0x55, 0xa0, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0x20, 0x3, 0x0, 0x0, 0x40, 0x0, 0x1, 0x0, 0xc8, 0x0, 0x0, 0x0, 0x59, 0x0, 0x0, 0x4, 0x46, 0x8e, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x12, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x12, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0x32, 0x10, 0x10, 0x0, 0x4, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0x32, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0xc2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0x32, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x67, 0x0, 0x0, 0x4, 0xf2, 0x20, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x2, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1a, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x42, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x1d, 0x0, 0x0, 0x7, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0xc0, 0x3e, 0x0, 0x0, 0x0, 0x7, 0x22, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbf, 0x36, 0x0, 0x0, 0x5, 0x42, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x32, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x46, 0x10, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x46, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xc2, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x6, 0x14, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0x6, 0x84, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x33, 0x0, 0x0, 0x7, 0x32, 0x0, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0xb6, 0xf, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x16, 0x5, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xc2, 0x0, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0x6, 0x14, 0x10, 0x0, 0x4, 0x0, 0x0, 0x0, 0x6, 0x84, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x33, 0x0, 0x0, 0x7, 0x32, 0x0, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0xb6, 0xf, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0x46, 0x0, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x32, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x46, 0x0, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0xbf, 0x0, 0x0, 0x80, 0xbf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x37, 0x0, 0x0, 0x9, 0x62, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x56, 0x6, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa6, 0x8, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1d, 0x0, 0x0, 0x7, 0x22, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e, 0x34, 0x0, 0x0, 0x7, 0x82, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0xa, 0x0, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x7, 0x82, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2a, 0x0, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x82, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x37, 0x0, 0x0, 0x9, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x86, 0x8, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0xe, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x32, 0x20, 0x10, 0x0, 0x1, 0x0, 0x0, 0x0, 0x86, 0x0, 0x10, 0x80, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6, 0xa, 0x10, 0x0, 0x3, 0x0, 0x0, 0x0, 0x32, 0x0, 0x0, 0xb, 0x32, 0x20, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x86, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x80, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6, 0x8a, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x8, 0xc2, 0x20, 0x10, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3f, 0x0, 0x0, 0x80, 0x3f, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x11, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0xf4, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xfe, 0xff, 0x0, 0x1, 0x0, 0x0, 0xcc, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x0, 0xab, 0xab, 0x3c, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x9c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xac, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xbc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x31, 0x36, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x0, 0xab, 0xab, 0x1, 0x0, 0x3, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5f, 0x31, 0x36, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x0, 0xab, 0x1, 0x0, 0x3, 0x0, 0x1, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0x49, 0x53, 0x47, 0x4e, 0x9c, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x89, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x90, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x90, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x90, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x0, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0xab, 0xab, 0xab, 0x4f, 0x53, 0x47, 0x4e, 0x80, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xc, 0x0, 0x0, 0x68, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x3, 0x0, 0x0, 0x68, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0xc, 0x0, 0x0, 0x71, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x0, 0x53, 0x56, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x0, 0xab, 0xab, 0xab}, - } -) diff --git a/vendor/gioui.org/gpu/timer.go b/vendor/gioui.org/gpu/timer.go index a1da948..c1e3227 100644 --- a/vendor/gioui.org/gpu/timer.go +++ b/vendor/gioui.org/gpu/timer.go @@ -5,18 +5,18 @@ package gpu import ( "time" - "gioui.org/gpu/backend" + "gioui.org/gpu/internal/driver" ) type timers struct { - backend backend.Device + backend driver.Device timers []*timer } type timer struct { Elapsed time.Duration - backend backend.Device - timer backend.Timer + backend driver.Device + timer driver.Timer state timerState } @@ -28,7 +28,7 @@ const ( timerWaiting ) -func newTimers(b backend.Device) *timers { +func newTimers(b driver.Device) *timers { return &timers{ backend: b, } @@ -83,7 +83,7 @@ func (t *timers) ready() bool { return t.backend.IsTimeContinuous() } -func (t *timers) release() { +func (t *timers) Release() { if t == nil { return } diff --git a/vendor/gioui.org/internal/byteslice/byteslice.go b/vendor/gioui.org/internal/byteslice/byteslice.go new file mode 100644 index 0000000..26ebdb2 --- /dev/null +++ b/vendor/gioui.org/internal/byteslice/byteslice.go @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +// Package byteslice provides byte slice views of other Go values such as +// slices and structs. +package byteslice + +import ( + "reflect" + "unsafe" +) + +// Struct returns a byte slice view of a struct. +func Struct(s interface{}) []byte { + v := reflect.ValueOf(s).Elem() + sz := int(v.Type().Size()) + var res []byte + h := (*reflect.SliceHeader)(unsafe.Pointer(&res)) + h.Data = uintptr(unsafe.Pointer(v.UnsafeAddr())) + h.Cap = sz + h.Len = sz + return res +} + +// Uint32 returns a byte slice view of a uint32 slice. +func Uint32(s []uint32) []byte { + n := len(s) + if n == 0 { + return nil + } + blen := n * int(unsafe.Sizeof(s[0])) + return (*[1 << 30]byte)(unsafe.Pointer(&s[0]))[:blen:blen] +} + +// Slice returns a byte slice view of a slice. +func Slice(s interface{}) []byte { + v := reflect.ValueOf(s) + first := v.Index(0) + sz := int(first.Type().Size()) + var res []byte + h := (*reflect.SliceHeader)(unsafe.Pointer(&res)) + h.Data = first.UnsafeAddr() + h.Cap = v.Cap() * sz + h.Len = v.Len() * sz + return res +} diff --git a/vendor/gioui.org/app/internal/cocoainit/cocoa_darwin.go b/vendor/gioui.org/internal/cocoainit/cocoa_darwin.go index 2a34e57..2a34e57 100644 --- a/vendor/gioui.org/app/internal/cocoainit/cocoa_darwin.go +++ b/vendor/gioui.org/internal/cocoainit/cocoa_darwin.go diff --git a/vendor/gioui.org/app/internal/d3d11/d3d11_windows.go b/vendor/gioui.org/internal/d3d11/d3d11_windows.go index 3dd2138..450fde1 100644 --- a/vendor/gioui.org/app/internal/d3d11/d3d11_windows.go +++ b/vendor/gioui.org/internal/d3d11/d3d11_windows.go @@ -13,9 +13,9 @@ import ( "golang.org/x/sys/windows" ) -type _DXGI_SWAP_CHAIN_DESC struct { - BufferDesc _DXGI_MODE_DESC - SampleDesc _DXGI_SAMPLE_DESC +type DXGI_SWAP_CHAIN_DESC struct { + BufferDesc DXGI_MODE_DESC + SampleDesc DXGI_SAMPLE_DESC BufferUsage uint32 BufferCount uint32 OutputWindow windows.Handle @@ -24,39 +24,39 @@ type _DXGI_SWAP_CHAIN_DESC struct { Flags uint32 } -type _DXGI_SAMPLE_DESC struct { +type DXGI_SAMPLE_DESC struct { Count uint32 Quality uint32 } -type _DXGI_MODE_DESC struct { +type DXGI_MODE_DESC struct { Width uint32 Height uint32 - RefreshRate _DXGI_RATIONAL + RefreshRate DXGI_RATIONAL Format uint32 ScanlineOrdering uint32 Scaling uint32 } -type _DXGI_RATIONAL struct { +type DXGI_RATIONAL struct { Numerator uint32 Denominator uint32 } -type _D3D11_TEXTURE2D_DESC struct { +type TEXTURE2D_DESC struct { Width uint32 Height uint32 MipLevels uint32 ArraySize uint32 Format uint32 - SampleDesc _DXGI_SAMPLE_DESC + SampleDesc DXGI_SAMPLE_DESC Usage uint32 BindFlags uint32 CPUAccessFlags uint32 MiscFlags uint32 } -type _D3D11_SAMPLER_DESC struct { +type SAMPLER_DESC struct { Filter uint32 AddressU uint32 AddressV uint32 @@ -69,22 +69,58 @@ type _D3D11_SAMPLER_DESC struct { MaxLOD float32 } -type _D3D11_SHADER_RESOURCE_VIEW_DESC_TEX2D struct { - _D3D11_SHADER_RESOURCE_VIEW_DESC - Texture2D _D3D11_TEX2D_SRV +type SHADER_RESOURCE_VIEW_DESC_TEX2D struct { + SHADER_RESOURCE_VIEW_DESC + Texture2D TEX2D_SRV } -type _D3D11_SHADER_RESOURCE_VIEW_DESC struct { +type SHADER_RESOURCE_VIEW_DESC_BUFFEREX struct { + SHADER_RESOURCE_VIEW_DESC + Buffer BUFFEREX_SRV +} + +type UNORDERED_ACCESS_VIEW_DESC_TEX2D struct { + UNORDERED_ACCESS_VIEW_DESC + Texture2D TEX2D_UAV +} + +type UNORDERED_ACCESS_VIEW_DESC_BUFFER struct { + UNORDERED_ACCESS_VIEW_DESC + Buffer BUFFER_UAV +} + +type SHADER_RESOURCE_VIEW_DESC struct { Format uint32 ViewDimension uint32 } -type _D3D11_TEX2D_SRV struct { +type UNORDERED_ACCESS_VIEW_DESC struct { + Format uint32 + ViewDimension uint32 +} + +type TEX2D_SRV struct { MostDetailedMip uint32 MipLevels uint32 } -type _D3D11_INPUT_ELEMENT_DESC struct { +type BUFFEREX_SRV struct { + FirstElement uint32 + NumElements uint32 + Flags uint32 +} + +type TEX2D_UAV struct { + MipSlice uint32 +} + +type BUFFER_UAV struct { + FirstElement uint32 + NumElements uint32 + Flags uint32 +} + +type INPUT_ELEMENT_DESC struct { SemanticName *byte SemanticIndex uint32 Format uint32 @@ -94,8 +130,8 @@ type _D3D11_INPUT_ELEMENT_DESC struct { InstanceDataStepRate uint32 } -type _IDXGISwapChain struct { - vtbl *struct { +type IDXGISwapChain struct { + Vtbl *struct { _IUnknownVTbl SetPrivateData uintptr SetPrivateDataInterface uintptr @@ -115,8 +151,23 @@ type _IDXGISwapChain struct { } } -type _ID3D11Device struct { - vtbl *struct { +type Debug struct { + Vtbl *struct { + _IUnknownVTbl + SetFeatureMask uintptr + GetFeatureMask uintptr + SetPresentPerRenderOpDelay uintptr + GetPresentPerRenderOpDelay uintptr + SetSwapChain uintptr + GetSwapChain uintptr + ValidateContext uintptr + ReportLiveDeviceObjects uintptr + ValidateContextForDispatch uintptr + } +} + +type Device struct { + Vtbl *struct { _IUnknownVTbl CreateBuffer uintptr CreateTexture1D uintptr @@ -161,8 +212,8 @@ type _ID3D11Device struct { } } -type _ID3D11DeviceContext struct { - vtbl *struct { +type DeviceContext struct { + Vtbl *struct { _IUnknownVTbl GetDevice uintptr GetPrivateData uintptr @@ -279,122 +330,134 @@ type _ID3D11DeviceContext struct { } } -type _ID3D11RenderTargetView struct { - vtbl *struct { +type RenderTargetView struct { + Vtbl *struct { + _IUnknownVTbl + } +} + +type Resource struct { + Vtbl *struct { _IUnknownVTbl } } -type _ID3D11Resource struct { - vtbl *struct { +type Texture2D struct { + Vtbl *struct { _IUnknownVTbl } } -type _ID3D11Texture2D struct { - vtbl *struct { +type Buffer struct { + Vtbl *struct { _IUnknownVTbl } } -type _ID3D11Buffer struct { - vtbl *struct { +type SamplerState struct { + Vtbl *struct { _IUnknownVTbl } } -type _ID3D11SamplerState struct { - vtbl *struct { +type PixelShader struct { + Vtbl *struct { _IUnknownVTbl } } -type _ID3D11PixelShader struct { - vtbl *struct { +type ShaderResourceView struct { + Vtbl *struct { _IUnknownVTbl } } -type _ID3D11ShaderResourceView struct { - vtbl *struct { +type UnorderedAccessView struct { + Vtbl *struct { _IUnknownVTbl } } -type _ID3D11DepthStencilView struct { - vtbl *struct { +type DepthStencilView struct { + Vtbl *struct { _IUnknownVTbl } } -type _ID3D11BlendState struct { - vtbl *struct { +type BlendState struct { + Vtbl *struct { _IUnknownVTbl } } -type _ID3D11DepthStencilState struct { - vtbl *struct { +type DepthStencilState struct { + Vtbl *struct { _IUnknownVTbl } } -type _ID3D11VertexShader struct { - vtbl *struct { +type VertexShader struct { + Vtbl *struct { _IUnknownVTbl } } -type _ID3D11RasterizerState struct { - vtbl *struct { +type ComputeShader struct { + Vtbl *struct { _IUnknownVTbl } } -type _ID3D11InputLayout struct { - vtbl *struct { +type RasterizerState struct { + Vtbl *struct { + _IUnknownVTbl + } +} + +type InputLayout struct { + Vtbl *struct { _IUnknownVTbl GetBufferPointer uintptr GetBufferSize uintptr } } -type _D3D11_DEPTH_STENCIL_DESC struct { +type DEPTH_STENCIL_DESC struct { DepthEnable uint32 DepthWriteMask uint32 DepthFunc uint32 StencilEnable uint32 StencilReadMask uint8 StencilWriteMask uint8 - FrontFace _D3D11_DEPTH_STENCILOP_DESC - BackFace _D3D11_DEPTH_STENCILOP_DESC + FrontFace DEPTH_STENCILOP_DESC + BackFace DEPTH_STENCILOP_DESC } -type _D3D11_DEPTH_STENCILOP_DESC struct { +type DEPTH_STENCILOP_DESC struct { StencilFailOp uint32 StencilDepthFailOp uint32 StencilPassOp uint32 StencilFunc uint32 } -type _D3D11_DEPTH_STENCIL_VIEW_DESC_TEX2D struct { +type DEPTH_STENCIL_VIEW_DESC_TEX2D struct { Format uint32 ViewDimension uint32 Flags uint32 - Texture2D _D3D11_TEX2D_DSV + Texture2D TEX2D_DSV } -type _D3D11_TEX2D_DSV struct { +type TEX2D_DSV struct { MipSlice uint32 } -type _D3D11_BLEND_DESC struct { +type BLEND_DESC struct { AlphaToCoverageEnable uint32 IndependentBlendEnable uint32 - RenderTarget [8]_D3D11_RENDER_TARGET_BLEND_DESC + RenderTarget [8]RENDER_TARGET_BLEND_DESC } -type _D3D11_RENDER_TARGET_BLEND_DESC struct { +type RENDER_TARGET_BLEND_DESC struct { BlendEnable uint32 SrcBlend uint32 DestBlend uint32 @@ -405,8 +468,8 @@ type _D3D11_RENDER_TARGET_BLEND_DESC struct { RenderTargetWriteMask uint8 } -type _IDXGIObject struct { - vtbl *struct { +type IDXGIObject struct { + Vtbl *struct { _IUnknownVTbl SetPrivateData uintptr SetPrivateDataInterface uintptr @@ -415,8 +478,8 @@ type _IDXGIObject struct { } } -type _IDXGIAdapter struct { - vtbl *struct { +type IDXGIAdapter struct { + Vtbl *struct { _IUnknownVTbl SetPrivateData uintptr SetPrivateDataInterface uintptr @@ -429,8 +492,8 @@ type _IDXGIAdapter struct { } } -type _IDXGIFactory struct { - vtbl *struct { +type IDXGIFactory struct { + Vtbl *struct { _IUnknownVTbl SetPrivateData uintptr SetPrivateDataInterface uintptr @@ -444,8 +507,15 @@ type _IDXGIFactory struct { } } -type _IDXGIDevice struct { - vtbl *struct { +type IDXGIDebug struct { + Vtbl *struct { + _IUnknownVTbl + ReportLiveObjects uintptr + } +} + +type IDXGIDevice struct { + Vtbl *struct { _IUnknownVTbl SetPrivateData uintptr SetPrivateDataInterface uintptr @@ -459,8 +529,8 @@ type _IDXGIDevice struct { } } -type _IUnknown struct { - vtbl *struct { +type IUnknown struct { + Vtbl *struct { _IUnknownVTbl } } @@ -471,7 +541,7 @@ type _IUnknownVTbl struct { Release uintptr } -type _D3D11_BUFFER_DESC struct { +type BUFFER_DESC struct { ByteWidth uint32 Usage uint32 BindFlags uint32 @@ -480,7 +550,7 @@ type _D3D11_BUFFER_DESC struct { StructureByteStride uint32 } -type _GUID struct { +type GUID struct { Data1 uint32 Data2 uint16 Data3 uint16 @@ -494,7 +564,7 @@ type _GUID struct { Data4_7 uint8 } -type _D3D11_VIEWPORT struct { +type VIEWPORT struct { TopLeftX float32 TopLeftY float32 Width float32 @@ -503,21 +573,21 @@ type _D3D11_VIEWPORT struct { MaxDepth float32 } -type _D3D11_SUBRESOURCE_DATA struct { +type SUBRESOURCE_DATA struct { pSysMem *byte } -type _D3D11_BOX struct { - left uint32 - top uint32 - front uint32 - right uint32 - bottom uint32 - back uint32 +type BOX struct { + Left uint32 + Top uint32 + Front uint32 + Right uint32 + Bottom uint32 + Back uint32 } -type _D3D11_MAPPED_SUBRESOURCE struct { - pData uintptr +type MAPPED_SUBRESOURCE struct { + PData uintptr RowPitch uint32 DepthPitch uint32 } @@ -527,7 +597,7 @@ type ErrorCode struct { Code uint32 } -type _D3D11_RASTERIZER_DESC struct { +type RASTERIZER_DESC struct { FillMode uint32 CullMode uint32 FrontCounterClockwise uint32 @@ -541,118 +611,146 @@ type _D3D11_RASTERIZER_DESC struct { } var ( - _IID_ID3D11Texture2D = _GUID{0x6f15aaf2, 0xd208, 0x4e89, 0x9a, 0xb4, 0x48, 0x95, 0x35, 0xd3, 0x4f, 0x9c} - _IID_IDXGIDevice = _GUID{0x54ec77fa, 0x1377, 0x44e6, 0x8c, 0x32, 0x88, 0xfd, 0x5f, 0x44, 0xc8, 0x4c} - _IID_IDXGIFactory = _GUID{0x7b7166ec, 0x21c7, 0x44ae, 0xb2, 0x1a, 0xc9, 0xae, 0x32, 0x1a, 0xe3, 0x69} + IID_Texture2D = GUID{0x6f15aaf2, 0xd208, 0x4e89, 0x9a, 0xb4, 0x48, 0x95, 0x35, 0xd3, 0x4f, 0x9c} + IID_IDXGIDebug = GUID{0x119E7452, 0xDE9E, 0x40fe, 0x88, 0x06, 0x88, 0xF9, 0x0C, 0x12, 0xB4, 0x41} + IID_IDXGIDevice = GUID{0x54ec77fa, 0x1377, 0x44e6, 0x8c, 0x32, 0x88, 0xfd, 0x5f, 0x44, 0xc8, 0x4c} + IID_IDXGIFactory = GUID{0x7b7166ec, 0x21c7, 0x44ae, 0xb2, 0x1a, 0xc9, 0xae, 0x32, 0x1a, 0xe3, 0x69} + IID_ID3D11Debug = GUID{0x79cf2233, 0x7536, 0x4948, 0x9d, 0x36, 0x1e, 0x46, 0x92, 0xdc, 0x57, 0x60} + + DXGI_DEBUG_ALL = GUID{0xe48ae283, 0xda80, 0x490b, 0x87, 0xe6, 0x43, 0xe9, 0xa9, 0xcf, 0xda, 0x8} ) var ( d3d11 = windows.NewLazySystemDLL("d3d11.dll") - __D3D11CreateDevice = d3d11.NewProc("D3D11CreateDevice") - __D3D11CreateDeviceAndSwapChain = d3d11.NewProc("D3D11CreateDeviceAndSwapChain") + _D3D11CreateDevice = d3d11.NewProc("D3D11CreateDevice") + _D3D11CreateDeviceAndSwapChain = d3d11.NewProc("D3D11CreateDeviceAndSwapChain") + + dxgi = windows.NewLazySystemDLL("dxgi.dll") + + _DXGIGetDebugInterface1 = dxgi.NewProc("DXGIGetDebugInterface1") ) const ( - _D3D11_SDK_VERSION = 7 - _D3D_DRIVER_TYPE_HARDWARE = 1 + SDK_VERSION = 7 + DRIVER_TYPE_HARDWARE = 1 - _DXGI_FORMAT_UNKNOWN = 0 - _DXGI_FORMAT_R16_FLOAT = 54 - _DXGI_FORMAT_R32_FLOAT = 41 - _DXGI_FORMAT_R32G32_FLOAT = 16 - _DXGI_FORMAT_R32G32B32_FLOAT = 6 - _DXGI_FORMAT_R32G32B32A32_FLOAT = 2 - _DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29 - _DXGI_FORMAT_R16_SINT = 59 - _DXGI_FORMAT_R16G16_SINT = 38 - _DXGI_FORMAT_R16_UINT = 57 - _DXGI_FORMAT_D24_UNORM_S8_UINT = 45 - _DXGI_FORMAT_R16G16_FLOAT = 34 - _DXGI_FORMAT_R16G16B16A16_FLOAT = 10 + DXGI_FORMAT_UNKNOWN = 0 + DXGI_FORMAT_R16_FLOAT = 54 + DXGI_FORMAT_R32_FLOAT = 41 + DXGI_FORMAT_R32_TYPELESS = 39 + DXGI_FORMAT_R32G32_FLOAT = 16 + DXGI_FORMAT_R32G32B32_FLOAT = 6 + DXGI_FORMAT_R32G32B32A32_FLOAT = 2 + DXGI_FORMAT_R8G8B8A8_UNORM = 28 + DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29 + DXGI_FORMAT_R16_SINT = 59 + DXGI_FORMAT_R16G16_SINT = 38 + DXGI_FORMAT_R16_UINT = 57 + DXGI_FORMAT_D24_UNORM_S8_UINT = 45 + DXGI_FORMAT_R16G16_FLOAT = 34 + DXGI_FORMAT_R16G16B16A16_FLOAT = 10 - _D3D11_FORMAT_SUPPORT_TEXTURE2D = 0x20 - _D3D11_FORMAT_SUPPORT_RENDER_TARGET = 0x4000 + DXGI_DEBUG_RLO_SUMMARY = 0x1 + DXGI_DEBUG_RLO_DETAIL = 0x2 + DXGI_DEBUG_RLO_IGNORE_INTERNAL = 0x4 - _DXGI_USAGE_RENDER_TARGET_OUTPUT = 1 << (1 + 4) + FORMAT_SUPPORT_TEXTURE2D = 0x20 + FORMAT_SUPPORT_RENDER_TARGET = 0x4000 - _D3D11_CPU_ACCESS_READ = 0x20000 + DXGI_USAGE_RENDER_TARGET_OUTPUT = 1 << (1 + 4) - _D3D11_MAP_READ = 1 + CPU_ACCESS_READ = 0x20000 - _DXGI_SWAP_EFFECT_DISCARD = 0 + MAP_READ = 1 - _D3D_FEATURE_LEVEL_9_1 = 0x9100 - _D3D_FEATURE_LEVEL_9_3 = 0x9300 - _D3D_FEATURE_LEVEL_11_0 = 0xb000 + DXGI_SWAP_EFFECT_DISCARD = 0 - _D3D11_USAGE_IMMUTABLE = 1 - _D3D11_USAGE_STAGING = 3 + FEATURE_LEVEL_9_1 = 0x9100 + FEATURE_LEVEL_9_3 = 0x9300 + FEATURE_LEVEL_11_0 = 0xb000 - _D3D11_BIND_VERTEX_BUFFER = 0x1 - _D3D11_BIND_INDEX_BUFFER = 0x2 - _D3D11_BIND_CONSTANT_BUFFER = 0x4 - _D3D11_BIND_SHADER_RESOURCE = 0x8 - _D3D11_BIND_RENDER_TARGET = 0x20 - _D3D11_BIND_DEPTH_STENCIL = 0x40 + USAGE_IMMUTABLE = 1 + USAGE_STAGING = 3 - _D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST = 4 - _D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP = 5 + BIND_VERTEX_BUFFER = 0x1 + BIND_INDEX_BUFFER = 0x2 + BIND_CONSTANT_BUFFER = 0x4 + BIND_SHADER_RESOURCE = 0x8 + BIND_RENDER_TARGET = 0x20 + BIND_DEPTH_STENCIL = 0x40 + BIND_UNORDERED_ACCESS = 0x80 - _D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT = 0x14 - _D3D11_FILTER_MIN_MAG_MIP_POINT = 0 + PRIMITIVE_TOPOLOGY_TRIANGLELIST = 4 + PRIMITIVE_TOPOLOGY_TRIANGLESTRIP = 5 - _D3D11_TEXTURE_ADDRESS_MIRROR = 2 - _D3D11_TEXTURE_ADDRESS_CLAMP = 3 - _D3D11_TEXTURE_ADDRESS_WRAP = 1 + FILTER_MIN_MAG_LINEAR_MIP_POINT = 0x14 + FILTER_MIN_MAG_MIP_POINT = 0 - _D3D11_SRV_DIMENSION_TEXTURE2D = 4 + TEXTURE_ADDRESS_MIRROR = 2 + TEXTURE_ADDRESS_CLAMP = 3 + TEXTURE_ADDRESS_WRAP = 1 - _D3D11_CREATE_DEVICE_DEBUG = 0x2 + SRV_DIMENSION_BUFFER = 1 + UAV_DIMENSION_BUFFER = 1 + SRV_DIMENSION_BUFFEREX = 11 + SRV_DIMENSION_TEXTURE2D = 4 + UAV_DIMENSION_TEXTURE2D = 4 - _D3D11_FILL_SOLID = 3 + BUFFER_UAV_FLAG_RAW = 0x1 + BUFFEREX_SRV_FLAG_RAW = 0x1 - _D3D11_CULL_NONE = 1 + RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS = 0x20 - _D3D11_CLEAR_DEPTH = 0x1 - _D3D11_CLEAR_STENCIL = 0x2 + CREATE_DEVICE_DEBUG = 0x2 - _D3D11_DSV_DIMENSION_TEXTURE2D = 3 + FILL_SOLID = 3 - _D3D11_DEPTH_WRITE_MASK_ALL = 1 + CULL_NONE = 1 - _D3D11_COMPARISON_GREATER = 5 - _D3D11_COMPARISON_GREATER_EQUAL = 7 + CLEAR_DEPTH = 0x1 + CLEAR_STENCIL = 0x2 - _D3D11_BLEND_OP_ADD = 1 - _D3D11_BLEND_ONE = 2 - _D3D11_BLEND_INV_SRC_ALPHA = 6 - _D3D11_BLEND_ZERO = 1 - _D3D11_BLEND_DEST_COLOR = 9 - _D3D11_BLEND_DEST_ALPHA = 7 + DSV_DIMENSION_TEXTURE2D = 3 - _D3D11_COLOR_WRITE_ENABLE_ALL = 1 | 2 | 4 | 8 + DEPTH_WRITE_MASK_ALL = 1 + + COMPARISON_GREATER = 5 + COMPARISON_GREATER_EQUAL = 7 + + BLEND_OP_ADD = 1 + BLEND_ONE = 2 + BLEND_INV_SRC_ALPHA = 6 + BLEND_ZERO = 1 + BLEND_DEST_COLOR = 9 + BLEND_DEST_ALPHA = 7 + + COLOR_WRITE_ENABLE_ALL = 1 | 2 | 4 | 8 DXGI_STATUS_OCCLUDED = 0x087A0001 DXGI_ERROR_DEVICE_RESET = 0x887A0007 DXGI_ERROR_DEVICE_REMOVED = 0x887A0005 D3DDDIERR_DEVICEREMOVED = 1<<31 | 0x876<<16 | 2160 + + RLDO_SUMMARY = 1 + RLDO_DETAIL = 2 + RLDO_IGNORE_INTERNAL = 4 ) -func _D3D11CreateDevice(driverType uint32, flags uint32) (*_ID3D11Device, *_ID3D11DeviceContext, uint32, error) { +func CreateDevice(driverType uint32, flags uint32) (*Device, *DeviceContext, uint32, error) { var ( - dev *_ID3D11Device - ctx *_ID3D11DeviceContext + dev *Device + ctx *DeviceContext featLvl uint32 ) - r, _, _ := __D3D11CreateDevice.Call( + r, _, _ := _D3D11CreateDevice.Call( 0, // pAdapter uintptr(driverType), // driverType 0, // Software uintptr(flags), // Flags 0, // pFeatureLevels 0, // FeatureLevels - _D3D11_SDK_VERSION, // SDKVersion + SDK_VERSION, // SDKVersion uintptr(unsafe.Pointer(&dev)), // ppDevice uintptr(unsafe.Pointer(&featLvl)), // pFeatureLevel uintptr(unsafe.Pointer(&ctx)), // ppImmediateContext @@ -663,21 +761,21 @@ func _D3D11CreateDevice(driverType uint32, flags uint32) (*_ID3D11Device, *_ID3D return dev, ctx, featLvl, nil } -func _D3D11CreateDeviceAndSwapChain(driverType uint32, flags uint32, swapDesc *_DXGI_SWAP_CHAIN_DESC) (*_ID3D11Device, *_ID3D11DeviceContext, *_IDXGISwapChain, uint32, error) { +func CreateDeviceAndSwapChain(driverType uint32, flags uint32, swapDesc *DXGI_SWAP_CHAIN_DESC) (*Device, *DeviceContext, *IDXGISwapChain, uint32, error) { var ( - dev *_ID3D11Device - ctx *_ID3D11DeviceContext - swchain *_IDXGISwapChain + dev *Device + ctx *DeviceContext + swchain *IDXGISwapChain featLvl uint32 ) - r, _, _ := __D3D11CreateDeviceAndSwapChain.Call( + r, _, _ := _D3D11CreateDeviceAndSwapChain.Call( 0, // pAdapter uintptr(driverType), // driverType 0, // Software uintptr(flags), // Flags 0, // pFeatureLevels 0, // FeatureLevels - _D3D11_SDK_VERSION, // SDKVersion + SDK_VERSION, // SDKVersion uintptr(unsafe.Pointer(swapDesc)), // pSwapChainDesc uintptr(unsafe.Pointer(&swchain)), // ppSwapChain uintptr(unsafe.Pointer(&dev)), // ppDevice @@ -690,31 +788,67 @@ func _D3D11CreateDeviceAndSwapChain(driverType uint32, flags uint32, swapDesc *_ return dev, ctx, swchain, featLvl, nil } -func (d *_ID3D11Device) CheckFormatSupport(format uint32) (uint32, error) { +func DXGIGetDebugInterface1() (*IDXGIDebug, error) { + var dbg *IDXGIDebug + r, _, _ := _DXGIGetDebugInterface1.Call( + 0, // Flags + uintptr(unsafe.Pointer(&IID_IDXGIDebug)), + uintptr(unsafe.Pointer(&dbg)), + ) + if r != 0 { + return nil, ErrorCode{Name: "DXGIGetDebugInterface1", Code: uint32(r)} + } + return dbg, nil +} + +func ReportLiveObjects() error { + dxgi, err := DXGIGetDebugInterface1() + if err != nil { + return err + } + defer IUnknownRelease(unsafe.Pointer(dxgi), dxgi.Vtbl.Release) + dxgi.ReportLiveObjects(&DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_DETAIL|DXGI_DEBUG_RLO_IGNORE_INTERNAL) + return nil +} + +func (d *IDXGIDebug) ReportLiveObjects(guid *GUID, flags uint32) { + syscall.Syscall6( + d.Vtbl.ReportLiveObjects, + 3, + uintptr(unsafe.Pointer(d)), + uintptr(unsafe.Pointer(guid)), + uintptr(flags), + 0, + 0, + 0, + ) +} + +func (d *Device) CheckFormatSupport(format uint32) (uint32, error) { var support uint32 r, _, _ := syscall.Syscall( - d.vtbl.CheckFormatSupport, + d.Vtbl.CheckFormatSupport, 3, uintptr(unsafe.Pointer(d)), uintptr(format), uintptr(unsafe.Pointer(&support)), ) if r != 0 { - return 0, ErrorCode{Name: "ID3D11DeviceCheckFormatSupport", Code: uint32(r)} + return 0, ErrorCode{Name: "DeviceCheckFormatSupport", Code: uint32(r)} } return support, nil } -func (d *_ID3D11Device) CreateBuffer(desc *_D3D11_BUFFER_DESC, data []byte) (*_ID3D11Buffer, error) { - var dataDesc *_D3D11_SUBRESOURCE_DATA +func (d *Device) CreateBuffer(desc *BUFFER_DESC, data []byte) (*Buffer, error) { + var dataDesc *SUBRESOURCE_DATA if len(data) > 0 { - dataDesc = &_D3D11_SUBRESOURCE_DATA{ + dataDesc = &SUBRESOURCE_DATA{ pSysMem: &data[0], } } - var buf *_ID3D11Buffer + var buf *Buffer r, _, _ := syscall.Syscall6( - d.vtbl.CreateBuffer, + d.Vtbl.CreateBuffer, 4, uintptr(unsafe.Pointer(d)), uintptr(unsafe.Pointer(desc)), @@ -723,15 +857,15 @@ func (d *_ID3D11Device) CreateBuffer(desc *_D3D11_BUFFER_DESC, data []byte) (*_I 0, 0, ) if r != 0 { - return nil, ErrorCode{Name: "ID3D11DeviceCreateBuffer", Code: uint32(r)} + return nil, ErrorCode{Name: "DeviceCreateBuffer", Code: uint32(r)} } return buf, nil } -func (d *_ID3D11Device) CreateDepthStencilViewTEX2D(res *_ID3D11Resource, desc *_D3D11_DEPTH_STENCIL_VIEW_DESC_TEX2D) (*_ID3D11DepthStencilView, error) { - var view *_ID3D11DepthStencilView +func (d *Device) CreateDepthStencilViewTEX2D(res *Resource, desc *DEPTH_STENCIL_VIEW_DESC_TEX2D) (*DepthStencilView, error) { + var view *DepthStencilView r, _, _ := syscall.Syscall6( - d.vtbl.CreateDepthStencilView, + d.Vtbl.CreateDepthStencilView, 4, uintptr(unsafe.Pointer(d)), uintptr(unsafe.Pointer(res)), @@ -740,15 +874,33 @@ func (d *_ID3D11Device) CreateDepthStencilViewTEX2D(res *_ID3D11Resource, desc * 0, 0, ) if r != 0 { - return nil, ErrorCode{Name: "ID3D11DeviceCreateDepthStencilView", Code: uint32(r)} + return nil, ErrorCode{Name: "DeviceCreateDepthStencilView", Code: uint32(r)} } return view, nil } -func (d *_ID3D11Device) CreatePixelShader(bytecode []byte) (*_ID3D11PixelShader, error) { - var shader *_ID3D11PixelShader +func (d *Device) CreatePixelShader(bytecode []byte) (*PixelShader, error) { + var shader *PixelShader + r, _, _ := syscall.Syscall6( + d.Vtbl.CreatePixelShader, + 5, + uintptr(unsafe.Pointer(d)), + uintptr(unsafe.Pointer(&bytecode[0])), + uintptr(len(bytecode)), + 0, // pClassLinkage + uintptr(unsafe.Pointer(&shader)), + 0, + ) + if r != 0 { + return nil, ErrorCode{Name: "DeviceCreatePixelShader", Code: uint32(r)} + } + return shader, nil +} + +func (d *Device) CreateVertexShader(bytecode []byte) (*VertexShader, error) { + var shader *VertexShader r, _, _ := syscall.Syscall6( - d.vtbl.CreatePixelShader, + d.Vtbl.CreateVertexShader, 5, uintptr(unsafe.Pointer(d)), uintptr(unsafe.Pointer(&bytecode[0])), @@ -758,15 +910,15 @@ func (d *_ID3D11Device) CreatePixelShader(bytecode []byte) (*_ID3D11PixelShader, 0, ) if r != 0 { - return nil, ErrorCode{Name: "ID3D11DeviceCreatePixelShader", Code: uint32(r)} + return nil, ErrorCode{Name: "DeviceCreateVertexShader", Code: uint32(r)} } return shader, nil } -func (d *_ID3D11Device) CreateVertexShader(bytecode []byte) (*_ID3D11VertexShader, error) { - var shader *_ID3D11VertexShader +func (d *Device) CreateComputeShader(bytecode []byte) (*ComputeShader, error) { + var shader *ComputeShader r, _, _ := syscall.Syscall6( - d.vtbl.CreateVertexShader, + d.Vtbl.CreateComputeShader, 5, uintptr(unsafe.Pointer(d)), uintptr(unsafe.Pointer(&bytecode[0])), @@ -776,51 +928,68 @@ func (d *_ID3D11Device) CreateVertexShader(bytecode []byte) (*_ID3D11VertexShade 0, ) if r != 0 { - return nil, ErrorCode{Name: "ID3D11DeviceCreateVertexShader", Code: uint32(r)} + return nil, ErrorCode{Name: "DeviceCreateComputeShader", Code: uint32(r)} } return shader, nil } -func (d *_ID3D11Device) CreateShaderResourceViewTEX2D(res *_ID3D11Resource, desc *_D3D11_SHADER_RESOURCE_VIEW_DESC_TEX2D) (*_ID3D11ShaderResourceView, error) { - var resView *_ID3D11ShaderResourceView +func (d *Device) CreateShaderResourceView(res *Resource, desc unsafe.Pointer) (*ShaderResourceView, error) { + var resView *ShaderResourceView r, _, _ := syscall.Syscall6( - d.vtbl.CreateShaderResourceView, + d.Vtbl.CreateShaderResourceView, 4, uintptr(unsafe.Pointer(d)), uintptr(unsafe.Pointer(res)), - uintptr(unsafe.Pointer(desc)), + uintptr(desc), uintptr(unsafe.Pointer(&resView)), 0, 0, ) if r != 0 { - return nil, ErrorCode{Name: "ID3D11DeviceCreateShaderResourceView", Code: uint32(r)} + return nil, ErrorCode{Name: "DeviceCreateShaderResourceView", Code: uint32(r)} } return resView, nil } -func (d *_ID3D11Device) CreateRasterizerState(desc *_D3D11_RASTERIZER_DESC) (*_ID3D11RasterizerState, error) { - var state *_ID3D11RasterizerState +func (d *Device) CreateUnorderedAccessView(res *Resource, desc unsafe.Pointer) (*UnorderedAccessView, error) { + var uaView *UnorderedAccessView + r, _, _ := syscall.Syscall6( + d.Vtbl.CreateUnorderedAccessView, + 4, + uintptr(unsafe.Pointer(d)), + uintptr(unsafe.Pointer(res)), + uintptr(desc), + uintptr(unsafe.Pointer(&uaView)), + 0, 0, + ) + if r != 0 { + return nil, ErrorCode{Name: "DeviceCreateUnorderedAccessView", Code: uint32(r)} + } + return uaView, nil +} + +func (d *Device) CreateRasterizerState(desc *RASTERIZER_DESC) (*RasterizerState, error) { + var state *RasterizerState r, _, _ := syscall.Syscall( - d.vtbl.CreateRasterizerState, + d.Vtbl.CreateRasterizerState, 3, uintptr(unsafe.Pointer(d)), uintptr(unsafe.Pointer(desc)), uintptr(unsafe.Pointer(&state)), ) if r != 0 { - return nil, ErrorCode{Name: "ID3D11DeviceCreateRasterizerState", Code: uint32(r)} + return nil, ErrorCode{Name: "DeviceCreateRasterizerState", Code: uint32(r)} } return state, nil } -func (d *_ID3D11Device) CreateInputLayout(descs []_D3D11_INPUT_ELEMENT_DESC, bytecode []byte) (*_ID3D11InputLayout, error) { - var pdesc *_D3D11_INPUT_ELEMENT_DESC +func (d *Device) CreateInputLayout(descs []INPUT_ELEMENT_DESC, bytecode []byte) (*InputLayout, error) { + var pdesc *INPUT_ELEMENT_DESC if len(descs) > 0 { pdesc = &descs[0] } - var layout *_ID3D11InputLayout + var layout *InputLayout r, _, _ := syscall.Syscall6( - d.vtbl.CreateInputLayout, + d.Vtbl.CreateInputLayout, 6, uintptr(unsafe.Pointer(d)), uintptr(unsafe.Pointer(pdesc)), @@ -830,30 +999,30 @@ func (d *_ID3D11Device) CreateInputLayout(descs []_D3D11_INPUT_ELEMENT_DESC, byt uintptr(unsafe.Pointer(&layout)), ) if r != 0 { - return nil, ErrorCode{Name: "ID3D11DeviceCreateInputLayout", Code: uint32(r)} + return nil, ErrorCode{Name: "DeviceCreateInputLayout", Code: uint32(r)} } return layout, nil } -func (d *_ID3D11Device) CreateSamplerState(desc *_D3D11_SAMPLER_DESC) (*_ID3D11SamplerState, error) { - var sampler *_ID3D11SamplerState +func (d *Device) CreateSamplerState(desc *SAMPLER_DESC) (*SamplerState, error) { + var sampler *SamplerState r, _, _ := syscall.Syscall( - d.vtbl.CreateSamplerState, + d.Vtbl.CreateSamplerState, 3, uintptr(unsafe.Pointer(d)), uintptr(unsafe.Pointer(desc)), uintptr(unsafe.Pointer(&sampler)), ) if r != 0 { - return nil, ErrorCode{Name: "ID3D11DeviceCreateSamplerState", Code: uint32(r)} + return nil, ErrorCode{Name: "DeviceCreateSamplerState", Code: uint32(r)} } return sampler, nil } -func (d *_ID3D11Device) CreateTexture2D(desc *_D3D11_TEXTURE2D_DESC) (*_ID3D11Texture2D, error) { - var tex *_ID3D11Texture2D +func (d *Device) CreateTexture2D(desc *TEXTURE2D_DESC) (*Texture2D, error) { + var tex *Texture2D r, _, _ := syscall.Syscall6( - d.vtbl.CreateTexture2D, + d.Vtbl.CreateTexture2D, 4, uintptr(unsafe.Pointer(d)), uintptr(unsafe.Pointer(desc)), @@ -862,15 +1031,15 @@ func (d *_ID3D11Device) CreateTexture2D(desc *_D3D11_TEXTURE2D_DESC) (*_ID3D11Te 0, 0, ) if r != 0 { - return nil, ErrorCode{Name: "ID3D11CreateTexture2D", Code: uint32(r)} + return nil, ErrorCode{Name: "CreateTexture2D", Code: uint32(r)} } return tex, nil } -func (d *_ID3D11Device) CreateRenderTargetView(res *_ID3D11Resource) (*_ID3D11RenderTargetView, error) { - var target *_ID3D11RenderTargetView +func (d *Device) CreateRenderTargetView(res *Resource) (*RenderTargetView, error) { + var target *RenderTargetView r, _, _ := syscall.Syscall6( - d.vtbl.CreateRenderTargetView, + d.Vtbl.CreateRenderTargetView, 4, uintptr(unsafe.Pointer(d)), uintptr(unsafe.Pointer(res)), @@ -879,59 +1048,102 @@ func (d *_ID3D11Device) CreateRenderTargetView(res *_ID3D11Resource) (*_ID3D11Re 0, 0, ) if r != 0 { - return nil, ErrorCode{Name: "ID3D11DeviceCreateRenderTargetView", Code: uint32(r)} + return nil, ErrorCode{Name: "DeviceCreateRenderTargetView", Code: uint32(r)} } return target, nil } -func (d *_ID3D11Device) CreateBlendState(desc *_D3D11_BLEND_DESC) (*_ID3D11BlendState, error) { - var state *_ID3D11BlendState +func (d *Device) CreateBlendState(desc *BLEND_DESC) (*BlendState, error) { + var state *BlendState r, _, _ := syscall.Syscall( - d.vtbl.CreateBlendState, + d.Vtbl.CreateBlendState, 3, uintptr(unsafe.Pointer(d)), uintptr(unsafe.Pointer(desc)), uintptr(unsafe.Pointer(&state)), ) if r != 0 { - return nil, ErrorCode{Name: "ID3D11DeviceCreateBlendState", Code: uint32(r)} + return nil, ErrorCode{Name: "DeviceCreateBlendState", Code: uint32(r)} } return state, nil } -func (d *_ID3D11Device) CreateDepthStencilState(desc *_D3D11_DEPTH_STENCIL_DESC) (*_ID3D11DepthStencilState, error) { - var state *_ID3D11DepthStencilState +func (d *Device) CreateDepthStencilState(desc *DEPTH_STENCIL_DESC) (*DepthStencilState, error) { + var state *DepthStencilState r, _, _ := syscall.Syscall( - d.vtbl.CreateDepthStencilState, + d.Vtbl.CreateDepthStencilState, 3, uintptr(unsafe.Pointer(d)), uintptr(unsafe.Pointer(desc)), uintptr(unsafe.Pointer(&state)), ) if r != 0 { - return nil, ErrorCode{Name: "ID3D11DeviceCreateDepthStencilState", Code: uint32(r)} + return nil, ErrorCode{Name: "DeviceCreateDepthStencilState", Code: uint32(r)} } return state, nil } -func (s *_IDXGISwapChain) GetDesc() (_DXGI_SWAP_CHAIN_DESC, error) { - var desc _DXGI_SWAP_CHAIN_DESC +func (d *Device) GetFeatureLevel() int { + lvl, _, _ := syscall.Syscall( + d.Vtbl.GetFeatureLevel, + 1, + uintptr(unsafe.Pointer(d)), + 0, 0, + ) + return int(lvl) +} + +func (d *Device) GetImmediateContext() *DeviceContext { + var ctx *DeviceContext + syscall.Syscall( + d.Vtbl.GetImmediateContext, + 2, + uintptr(unsafe.Pointer(d)), + uintptr(unsafe.Pointer(&ctx)), + 0, + ) + return ctx +} + +func (d *Device) ReportLiveDeviceObjects() error { + intf, err := IUnknownQueryInterface(unsafe.Pointer(d), d.Vtbl.QueryInterface, &IID_ID3D11Debug) + if err != nil { + return fmt.Errorf("ReportLiveObjects: failed to query ID3D11Debug interface: %v", err) + } + defer IUnknownRelease(unsafe.Pointer(intf), intf.Vtbl.Release) + dbg := (*Debug)(unsafe.Pointer(intf)) + dbg.ReportLiveDeviceObjects(RLDO_DETAIL | RLDO_IGNORE_INTERNAL) + return nil +} + +func (d *Debug) ReportLiveDeviceObjects(flags uint32) { + syscall.Syscall( + d.Vtbl.ReportLiveDeviceObjects, + 2, + uintptr(unsafe.Pointer(d)), + uintptr(flags), + 0, + ) +} + +func (s *IDXGISwapChain) GetDesc() (DXGI_SWAP_CHAIN_DESC, error) { + var desc DXGI_SWAP_CHAIN_DESC r, _, _ := syscall.Syscall( - s.vtbl.GetDesc, + s.Vtbl.GetDesc, 2, uintptr(unsafe.Pointer(s)), uintptr(unsafe.Pointer(&desc)), 0, ) if r != 0 { - return _DXGI_SWAP_CHAIN_DESC{}, ErrorCode{Name: "IDXGISwapChainGetDesc", Code: uint32(r)} + return DXGI_SWAP_CHAIN_DESC{}, ErrorCode{Name: "IDXGISwapChainGetDesc", Code: uint32(r)} } return desc, nil } -func (s *_IDXGISwapChain) ResizeBuffers(buffers, width, height, newFormat, flags uint32) error { +func (s *IDXGISwapChain) ResizeBuffers(buffers, width, height, newFormat, flags uint32) error { r, _, _ := syscall.Syscall6( - s.vtbl.ResizeBuffers, + s.Vtbl.ResizeBuffers, 6, uintptr(unsafe.Pointer(s)), uintptr(buffers), @@ -946,9 +1158,9 @@ func (s *_IDXGISwapChain) ResizeBuffers(buffers, width, height, newFormat, flags return nil } -func (s *_IDXGISwapChain) Present(SyncInterval int, Flags uint32) error { +func (s *IDXGISwapChain) Present(SyncInterval int, Flags uint32) error { r, _, _ := syscall.Syscall( - s.vtbl.Present, + s.Vtbl.Present, 3, uintptr(unsafe.Pointer(s)), uintptr(SyncInterval), @@ -960,10 +1172,10 @@ func (s *_IDXGISwapChain) Present(SyncInterval int, Flags uint32) error { return nil } -func (s *_IDXGISwapChain) GetBuffer(index int, riid *_GUID) (*_IUnknown, error) { - var buf *_IUnknown +func (s *IDXGISwapChain) GetBuffer(index int, riid *GUID) (*IUnknown, error) { + var buf *IUnknown r, _, _ := syscall.Syscall6( - s.vtbl.GetBuffer, + s.Vtbl.GetBuffer, 4, uintptr(unsafe.Pointer(s)), uintptr(index), @@ -978,9 +1190,9 @@ func (s *_IDXGISwapChain) GetBuffer(index int, riid *_GUID) (*_IUnknown, error) return buf, nil } -func (c *_ID3D11DeviceContext) Unmap(resource *_ID3D11Resource, subResource uint32) { +func (c *DeviceContext) Unmap(resource *Resource, subResource uint32) { syscall.Syscall( - c.vtbl.Unmap, + c.Vtbl.Unmap, 3, uintptr(unsafe.Pointer(c)), uintptr(unsafe.Pointer(resource)), @@ -988,10 +1200,10 @@ func (c *_ID3D11DeviceContext) Unmap(resource *_ID3D11Resource, subResource uint ) } -func (c *_ID3D11DeviceContext) Map(resource *_ID3D11Resource, subResource, mapType, mapFlags uint32) (_D3D11_MAPPED_SUBRESOURCE, error) { - var resMap _D3D11_MAPPED_SUBRESOURCE +func (c *DeviceContext) Map(resource *Resource, subResource, mapType, mapFlags uint32) (MAPPED_SUBRESOURCE, error) { + var resMap MAPPED_SUBRESOURCE r, _, _ := syscall.Syscall6( - c.vtbl.Map, + c.Vtbl.Map, 6, uintptr(unsafe.Pointer(c)), uintptr(unsafe.Pointer(resource)), @@ -1001,14 +1213,14 @@ func (c *_ID3D11DeviceContext) Map(resource *_ID3D11Resource, subResource, mapTy uintptr(unsafe.Pointer(&resMap)), ) if r != 0 { - return resMap, ErrorCode{Name: "ID3D11DeviceContextMap", Code: uint32(r)} + return resMap, ErrorCode{Name: "DeviceContextMap", Code: uint32(r)} } return resMap, nil } -func (c *_ID3D11DeviceContext) CopySubresourceRegion(dst *_ID3D11Resource, dstSubresource, dstX, dstY, dstZ uint32, src *_ID3D11Resource, srcSubresource uint32, srcBox *_D3D11_BOX) { +func (c *DeviceContext) CopySubresourceRegion(dst *Resource, dstSubresource, dstX, dstY, dstZ uint32, src *Resource, srcSubresource uint32, srcBox *BOX) { syscall.Syscall9( - c.vtbl.CopySubresourceRegion, + c.Vtbl.CopySubresourceRegion, 9, uintptr(unsafe.Pointer(c)), uintptr(unsafe.Pointer(dst)), @@ -1022,9 +1234,9 @@ func (c *_ID3D11DeviceContext) CopySubresourceRegion(dst *_ID3D11Resource, dstSu ) } -func (c *_ID3D11DeviceContext) ClearDepthStencilView(target *_ID3D11DepthStencilView, flags uint32, depth float32, stencil uint8) { +func (c *DeviceContext) ClearDepthStencilView(target *DepthStencilView, flags uint32, depth float32, stencil uint8) { syscall.Syscall6( - c.vtbl.ClearDepthStencilView, + c.Vtbl.ClearDepthStencilView, 5, uintptr(unsafe.Pointer(c)), uintptr(unsafe.Pointer(target)), @@ -1035,9 +1247,9 @@ func (c *_ID3D11DeviceContext) ClearDepthStencilView(target *_ID3D11DepthStencil ) } -func (c *_ID3D11DeviceContext) ClearRenderTargetView(target *_ID3D11RenderTargetView, color *[4]float32) { +func (c *DeviceContext) ClearRenderTargetView(target *RenderTargetView, color *[4]float32) { syscall.Syscall( - c.vtbl.ClearRenderTargetView, + c.Vtbl.ClearRenderTargetView, 3, uintptr(unsafe.Pointer(c)), uintptr(unsafe.Pointer(target)), @@ -1045,9 +1257,45 @@ func (c *_ID3D11DeviceContext) ClearRenderTargetView(target *_ID3D11RenderTarget ) } -func (c *_ID3D11DeviceContext) RSSetViewports(viewport *_D3D11_VIEWPORT) { +func (c *DeviceContext) CSSetShaderResources(startSlot uint32, s *ShaderResourceView) { + syscall.Syscall6( + c.Vtbl.CSSetShaderResources, + 4, + uintptr(unsafe.Pointer(c)), + uintptr(startSlot), + 1, // NumViews + uintptr(unsafe.Pointer(&s)), + 0, 0, + ) +} + +func (c *DeviceContext) CSSetUnorderedAccessViews(startSlot uint32, v *UnorderedAccessView) { + syscall.Syscall6( + c.Vtbl.CSSetUnorderedAccessViews, + 4, + uintptr(unsafe.Pointer(c)), + uintptr(startSlot), + 1, // NumViews + uintptr(unsafe.Pointer(&v)), + 0, 0, + ) +} + +func (c *DeviceContext) CSSetShader(s *ComputeShader) { + syscall.Syscall6( + c.Vtbl.CSSetShader, + 4, + uintptr(unsafe.Pointer(c)), + uintptr(unsafe.Pointer(s)), + 0, // ppClassInstances + 0, // NumClassInstances + 0, 0, + ) +} + +func (c *DeviceContext) RSSetViewports(viewport *VIEWPORT) { syscall.Syscall( - c.vtbl.RSSetViewports, + c.Vtbl.RSSetViewports, 3, uintptr(unsafe.Pointer(c)), 1, // NumViewports @@ -1055,9 +1303,9 @@ func (c *_ID3D11DeviceContext) RSSetViewports(viewport *_D3D11_VIEWPORT) { ) } -func (c *_ID3D11DeviceContext) VSSetShader(s *_ID3D11VertexShader) { +func (c *DeviceContext) VSSetShader(s *VertexShader) { syscall.Syscall6( - c.vtbl.VSSetShader, + c.Vtbl.VSSetShader, 4, uintptr(unsafe.Pointer(c)), uintptr(unsafe.Pointer(s)), @@ -1067,9 +1315,9 @@ func (c *_ID3D11DeviceContext) VSSetShader(s *_ID3D11VertexShader) { ) } -func (c *_ID3D11DeviceContext) VSSetConstantBuffers(b *_ID3D11Buffer) { +func (c *DeviceContext) VSSetConstantBuffers(b *Buffer) { syscall.Syscall6( - c.vtbl.VSSetConstantBuffers, + c.Vtbl.VSSetConstantBuffers, 4, uintptr(unsafe.Pointer(c)), 0, // StartSlot @@ -1079,9 +1327,9 @@ func (c *_ID3D11DeviceContext) VSSetConstantBuffers(b *_ID3D11Buffer) { ) } -func (c *_ID3D11DeviceContext) PSSetConstantBuffers(b *_ID3D11Buffer) { +func (c *DeviceContext) PSSetConstantBuffers(b *Buffer) { syscall.Syscall6( - c.vtbl.PSSetConstantBuffers, + c.Vtbl.PSSetConstantBuffers, 4, uintptr(unsafe.Pointer(c)), 0, // StartSlot @@ -1091,9 +1339,9 @@ func (c *_ID3D11DeviceContext) PSSetConstantBuffers(b *_ID3D11Buffer) { ) } -func (c *_ID3D11DeviceContext) PSSetShaderResources(startSlot uint32, s *_ID3D11ShaderResourceView) { +func (c *DeviceContext) PSSetShaderResources(startSlot uint32, s *ShaderResourceView) { syscall.Syscall6( - c.vtbl.PSSetShaderResources, + c.Vtbl.PSSetShaderResources, 4, uintptr(unsafe.Pointer(c)), uintptr(startSlot), @@ -1103,9 +1351,9 @@ func (c *_ID3D11DeviceContext) PSSetShaderResources(startSlot uint32, s *_ID3D11 ) } -func (c *_ID3D11DeviceContext) PSSetSamplers(startSlot uint32, s *_ID3D11SamplerState) { +func (c *DeviceContext) PSSetSamplers(startSlot uint32, s *SamplerState) { syscall.Syscall6( - c.vtbl.PSSetSamplers, + c.Vtbl.PSSetSamplers, 4, uintptr(unsafe.Pointer(c)), uintptr(startSlot), @@ -1115,9 +1363,9 @@ func (c *_ID3D11DeviceContext) PSSetSamplers(startSlot uint32, s *_ID3D11Sampler ) } -func (c *_ID3D11DeviceContext) PSSetShader(s *_ID3D11PixelShader) { +func (c *DeviceContext) PSSetShader(s *PixelShader) { syscall.Syscall6( - c.vtbl.PSSetShader, + c.Vtbl.PSSetShader, 4, uintptr(unsafe.Pointer(c)), uintptr(unsafe.Pointer(s)), @@ -1127,14 +1375,14 @@ func (c *_ID3D11DeviceContext) PSSetShader(s *_ID3D11PixelShader) { ) } -func (c *_ID3D11DeviceContext) UpdateSubresource(res *_ID3D11Resource, rowPitch, depthPitch uint32, data []byte) { +func (c *DeviceContext) UpdateSubresource(res *Resource, dstBox *BOX, rowPitch, depthPitch uint32, data []byte) { syscall.Syscall9( - c.vtbl.UpdateSubresource, + c.Vtbl.UpdateSubresource, 7, uintptr(unsafe.Pointer(c)), uintptr(unsafe.Pointer(res)), 0, // DstSubresource - 0, // pDstBox + uintptr(unsafe.Pointer(dstBox)), uintptr(unsafe.Pointer(&data[0])), uintptr(rowPitch), uintptr(depthPitch), @@ -1142,9 +1390,9 @@ func (c *_ID3D11DeviceContext) UpdateSubresource(res *_ID3D11Resource, rowPitch, ) } -func (c *_ID3D11DeviceContext) RSSetState(state *_ID3D11RasterizerState) { +func (c *DeviceContext) RSSetState(state *RasterizerState) { syscall.Syscall( - c.vtbl.RSSetState, + c.Vtbl.RSSetState, 2, uintptr(unsafe.Pointer(c)), uintptr(unsafe.Pointer(state)), @@ -1152,9 +1400,9 @@ func (c *_ID3D11DeviceContext) RSSetState(state *_ID3D11RasterizerState) { ) } -func (c *_ID3D11DeviceContext) IASetInputLayout(layout *_ID3D11InputLayout) { +func (c *DeviceContext) IASetInputLayout(layout *InputLayout) { syscall.Syscall( - c.vtbl.IASetInputLayout, + c.Vtbl.IASetInputLayout, 2, uintptr(unsafe.Pointer(c)), uintptr(unsafe.Pointer(layout)), @@ -1162,9 +1410,9 @@ func (c *_ID3D11DeviceContext) IASetInputLayout(layout *_ID3D11InputLayout) { ) } -func (c *_ID3D11DeviceContext) IASetIndexBuffer(buf *_ID3D11Buffer, format, offset uint32) { +func (c *DeviceContext) IASetIndexBuffer(buf *Buffer, format, offset uint32) { syscall.Syscall6( - c.vtbl.IASetIndexBuffer, + c.Vtbl.IASetIndexBuffer, 4, uintptr(unsafe.Pointer(c)), uintptr(unsafe.Pointer(buf)), @@ -1174,9 +1422,9 @@ func (c *_ID3D11DeviceContext) IASetIndexBuffer(buf *_ID3D11Buffer, format, offs ) } -func (c *_ID3D11DeviceContext) IASetVertexBuffers(buf *_ID3D11Buffer, stride, offset uint32) { +func (c *DeviceContext) IASetVertexBuffers(buf *Buffer, stride, offset uint32) { syscall.Syscall6( - c.vtbl.IASetVertexBuffers, + c.Vtbl.IASetVertexBuffers, 6, uintptr(unsafe.Pointer(c)), 0, // StartSlot @@ -1187,9 +1435,9 @@ func (c *_ID3D11DeviceContext) IASetVertexBuffers(buf *_ID3D11Buffer, stride, of ) } -func (c *_ID3D11DeviceContext) IASetPrimitiveTopology(mode uint32) { +func (c *DeviceContext) IASetPrimitiveTopology(mode uint32) { syscall.Syscall( - c.vtbl.IASetPrimitiveTopology, + c.Vtbl.IASetPrimitiveTopology, 2, uintptr(unsafe.Pointer(c)), uintptr(mode), @@ -1197,23 +1445,26 @@ func (c *_ID3D11DeviceContext) IASetPrimitiveTopology(mode uint32) { ) } -func (c *_ID3D11DeviceContext) OMGetRenderTargets() *_ID3D11RenderTargetView { - var target *_ID3D11RenderTargetView +func (c *DeviceContext) OMGetRenderTargets() (*RenderTargetView, *DepthStencilView) { + var ( + target *RenderTargetView + depthStencilView *DepthStencilView + ) syscall.Syscall6( - c.vtbl.OMGetRenderTargets, + c.Vtbl.OMGetRenderTargets, 4, uintptr(unsafe.Pointer(c)), 1, // NumViews uintptr(unsafe.Pointer(&target)), - 0, // pDepthStencilView + uintptr(unsafe.Pointer(&depthStencilView)), 0, 0, ) - return target + return target, depthStencilView } -func (c *_ID3D11DeviceContext) OMSetRenderTargets(target *_ID3D11RenderTargetView, depthStencil *_ID3D11DepthStencilView) { +func (c *DeviceContext) OMSetRenderTargets(target *RenderTargetView, depthStencil *DepthStencilView) { syscall.Syscall6( - c.vtbl.OMSetRenderTargets, + c.Vtbl.OMSetRenderTargets, 4, uintptr(unsafe.Pointer(c)), 1, // NumViews @@ -1223,9 +1474,9 @@ func (c *_ID3D11DeviceContext) OMSetRenderTargets(target *_ID3D11RenderTargetVie ) } -func (c *_ID3D11DeviceContext) Draw(count, start uint32) { +func (c *DeviceContext) Draw(count, start uint32) { syscall.Syscall( - c.vtbl.Draw, + c.Vtbl.Draw, 3, uintptr(unsafe.Pointer(c)), uintptr(count), @@ -1233,9 +1484,9 @@ func (c *_ID3D11DeviceContext) Draw(count, start uint32) { ) } -func (c *_ID3D11DeviceContext) DrawIndexed(count, start uint32, base int32) { +func (c *DeviceContext) DrawIndexed(count, start uint32, base int32) { syscall.Syscall6( - c.vtbl.DrawIndexed, + c.Vtbl.DrawIndexed, 4, uintptr(unsafe.Pointer(c)), uintptr(count), @@ -1245,9 +1496,21 @@ func (c *_ID3D11DeviceContext) DrawIndexed(count, start uint32, base int32) { ) } -func (c *_ID3D11DeviceContext) OMSetBlendState(state *_ID3D11BlendState, factor *f32color.RGBA, sampleMask uint32) { +func (c *DeviceContext) Dispatch(x, y, z uint32) { + syscall.Syscall6( + c.Vtbl.Dispatch, + 4, + uintptr(unsafe.Pointer(c)), + uintptr(x), + uintptr(y), + uintptr(z), + 0, 0, + ) +} + +func (c *DeviceContext) OMSetBlendState(state *BlendState, factor *f32color.RGBA, sampleMask uint32) { syscall.Syscall6( - c.vtbl.OMSetBlendState, + c.Vtbl.OMSetBlendState, 4, uintptr(unsafe.Pointer(c)), uintptr(unsafe.Pointer(state)), @@ -1257,9 +1520,9 @@ func (c *_ID3D11DeviceContext) OMSetBlendState(state *_ID3D11BlendState, factor ) } -func (c *_ID3D11DeviceContext) OMSetDepthStencilState(state *_ID3D11DepthStencilState, stencilRef uint32) { +func (c *DeviceContext) OMSetDepthStencilState(state *DepthStencilState, stencilRef uint32) { syscall.Syscall( - c.vtbl.OMSetDepthStencilState, + c.Vtbl.OMSetDepthStencilState, 3, uintptr(unsafe.Pointer(c)), uintptr(unsafe.Pointer(state)), @@ -1267,10 +1530,10 @@ func (c *_ID3D11DeviceContext) OMSetDepthStencilState(state *_ID3D11DepthStencil ) } -func (d *_IDXGIObject) GetParent(guid *_GUID) (*_IDXGIObject, error) { - var parent *_IDXGIObject +func (d *IDXGIObject) GetParent(guid *GUID) (*IDXGIObject, error) { + var parent *IDXGIObject r, _, _ := syscall.Syscall( - d.vtbl.GetParent, + d.Vtbl.GetParent, 3, uintptr(unsafe.Pointer(d)), uintptr(unsafe.Pointer(guid)), @@ -1282,10 +1545,10 @@ func (d *_IDXGIObject) GetParent(guid *_GUID) (*_IDXGIObject, error) { return parent, nil } -func (d *_IDXGIFactory) CreateSwapChain(device *_IUnknown, desc *_DXGI_SWAP_CHAIN_DESC) (*_IDXGISwapChain, error) { - var swchain *_IDXGISwapChain +func (d *IDXGIFactory) CreateSwapChain(device *IUnknown, desc *DXGI_SWAP_CHAIN_DESC) (*IDXGISwapChain, error) { + var swchain *IDXGISwapChain r, _, _ := syscall.Syscall6( - d.vtbl.CreateSwapChain, + d.Vtbl.CreateSwapChain, 4, uintptr(unsafe.Pointer(d)), uintptr(unsafe.Pointer(device)), @@ -1299,10 +1562,10 @@ func (d *_IDXGIFactory) CreateSwapChain(device *_IUnknown, desc *_DXGI_SWAP_CHAI return swchain, nil } -func (d *_IDXGIDevice) GetAdapter() (*_IDXGIAdapter, error) { - var adapter *_IDXGIAdapter +func (d *IDXGIDevice) GetAdapter() (*IDXGIAdapter, error) { + var adapter *IDXGIAdapter r, _, _ := syscall.Syscall( - d.vtbl.GetAdapter, + d.Vtbl.GetAdapter, 2, uintptr(unsafe.Pointer(d)), uintptr(unsafe.Pointer(&adapter)), @@ -1314,8 +1577,8 @@ func (d *_IDXGIDevice) GetAdapter() (*_IDXGIAdapter, error) { return adapter, nil } -func _IUnknownQueryInterface(obj unsafe.Pointer, queryInterfaceMethod uintptr, guid *_GUID) (*_IUnknown, error) { - var ref *_IUnknown +func IUnknownQueryInterface(obj unsafe.Pointer, queryInterfaceMethod uintptr, guid *GUID) (*IUnknown, error) { + var ref *IUnknown r, _, _ := syscall.Syscall( queryInterfaceMethod, 3, @@ -1329,7 +1592,17 @@ func _IUnknownQueryInterface(obj unsafe.Pointer, queryInterfaceMethod uintptr, g return ref, nil } -func _IUnknownRelease(obj unsafe.Pointer, releaseMethod uintptr) { +func IUnknownAddRef(obj unsafe.Pointer, addRefMethod uintptr) { + syscall.Syscall( + addRefMethod, + 1, + uintptr(obj), + 0, + 0, + ) +} + +func IUnknownRelease(obj unsafe.Pointer, releaseMethod uintptr) { syscall.Syscall( releaseMethod, 1, @@ -1342,3 +1615,68 @@ func _IUnknownRelease(obj unsafe.Pointer, releaseMethod uintptr) { func (e ErrorCode) Error() string { return fmt.Sprintf("%s: %#x", e.Name, e.Code) } + +func CreateSwapChain(dev *Device, hwnd windows.Handle) (*IDXGISwapChain, error) { + dxgiDev, err := IUnknownQueryInterface(unsafe.Pointer(dev), dev.Vtbl.QueryInterface, &IID_IDXGIDevice) + if err != nil { + return nil, fmt.Errorf("NewContext: %v", err) + } + adapter, err := (*IDXGIDevice)(unsafe.Pointer(dxgiDev)).GetAdapter() + IUnknownRelease(unsafe.Pointer(dxgiDev), dxgiDev.Vtbl.Release) + if err != nil { + return nil, fmt.Errorf("NewContext: %v", err) + } + dxgiFactory, err := (*IDXGIObject)(unsafe.Pointer(adapter)).GetParent(&IID_IDXGIFactory) + IUnknownRelease(unsafe.Pointer(adapter), adapter.Vtbl.Release) + if err != nil { + return nil, fmt.Errorf("NewContext: %v", err) + } + swchain, err := (*IDXGIFactory)(unsafe.Pointer(dxgiFactory)).CreateSwapChain( + (*IUnknown)(unsafe.Pointer(dev)), + &DXGI_SWAP_CHAIN_DESC{ + BufferDesc: DXGI_MODE_DESC{ + Format: DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, + }, + SampleDesc: DXGI_SAMPLE_DESC{ + Count: 1, + }, + BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT, + BufferCount: 1, + OutputWindow: hwnd, + Windowed: 1, + SwapEffect: DXGI_SWAP_EFFECT_DISCARD, + }, + ) + IUnknownRelease(unsafe.Pointer(dxgiFactory), dxgiFactory.Vtbl.Release) + if err != nil { + return nil, fmt.Errorf("NewContext: %v", err) + } + return swchain, nil +} + +func CreateDepthView(d *Device, width, height, depthBits int) (*DepthStencilView, error) { + depthTex, err := d.CreateTexture2D(&TEXTURE2D_DESC{ + Width: uint32(width), + Height: uint32(height), + MipLevels: 1, + ArraySize: 1, + Format: DXGI_FORMAT_D24_UNORM_S8_UINT, + SampleDesc: DXGI_SAMPLE_DESC{ + Count: 1, + Quality: 0, + }, + BindFlags: BIND_DEPTH_STENCIL, + }) + if err != nil { + return nil, err + } + depthView, err := d.CreateDepthStencilViewTEX2D( + (*Resource)(unsafe.Pointer(depthTex)), + &DEPTH_STENCIL_VIEW_DESC_TEX2D{ + Format: DXGI_FORMAT_D24_UNORM_S8_UINT, + ViewDimension: DSV_DIMENSION_TEXTURE2D, + }, + ) + IUnknownRelease(unsafe.Pointer(depthTex), depthTex.Vtbl.Release) + return depthView, err +} diff --git a/vendor/gioui.org/app/internal/egl/egl.go b/vendor/gioui.org/internal/egl/egl.go index f1d2595..7d7b551 100644 --- a/vendor/gioui.org/app/internal/egl/egl.go +++ b/vendor/gioui.org/internal/egl/egl.go @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Unlicense OR MIT +//go:build linux || windows || freebsd || openbsd // +build linux windows freebsd openbsd package egl @@ -10,21 +11,14 @@ import ( "runtime" "strings" - "gioui.org/app/internal/glimpl" - "gioui.org/app/internal/srgb" - "gioui.org/gpu/backend" - "gioui.org/gpu/gl" + "gioui.org/gpu" ) type Context struct { - c *glimpl.Functions disp _EGLDisplay eglCtx *eglContext eglSurf _EGLSurface width, height int - refreshFBO bool - // For sRGB emulation. - srgbFBO *srgb.FBO } type eglContext struct { @@ -63,30 +57,18 @@ const ( ) func (c *Context) Release() { - if c.srgbFBO != nil { - c.srgbFBO.Release() - c.srgbFBO = nil - } c.ReleaseSurface() if c.eglCtx != nil { eglDestroyContext(c.disp, c.eglCtx.ctx) - eglTerminate(c.disp) - eglReleaseThread() c.eglCtx = nil } c.disp = nilEGLDisplay } func (c *Context) Present() error { - if c.srgbFBO != nil { - c.srgbFBO.Blit() - } if !eglSwapBuffers(c.disp, c.eglSurf) { return fmt.Errorf("eglSwapBuffers failed (%x)", eglGetError()) } - if c.srgbFBO != nil { - c.srgbFBO.AfterPresent() - } return nil } @@ -111,17 +93,16 @@ func NewContext(disp NativeDisplayType) (*Context, error) { c := &Context{ disp: eglDisp, eglCtx: eglCtx, - c: new(glimpl.Functions), } return c, nil } -func (c *Context) Functions() *glimpl.Functions { - return c.c +func (c *Context) RenderTarget() (gpu.RenderTarget, error) { + return gpu.OpenGLRenderTarget{}, nil } -func (c *Context) Backend() (backend.Device, error) { - return gl.NewBackend(c.c) +func (c *Context) API() gpu.API { + return gpu.OpenGL{} } func (c *Context) ReleaseSurface() { @@ -129,7 +110,7 @@ func (c *Context) ReleaseSurface() { return } // Make sure any in-flight GL commands are complete. - c.c.Finish() + eglWaitClient() c.ReleaseCurrent() eglDestroySurface(c.disp, c.eglSurf) c.eglSurf = nilEGLSurface @@ -144,7 +125,6 @@ func (c *Context) CreateSurface(win NativeWindowType, width, height int) error { c.eglSurf = eglSurf c.width = width c.height = height - c.refreshFBO = true return err } @@ -155,26 +135,15 @@ func (c *Context) ReleaseCurrent() { } func (c *Context) MakeCurrent() error { + // OpenGL contexts are implicit and thread-local. Lock the OS thread. + runtime.LockOSThread() + if c.eglSurf == nilEGLSurface && !c.eglCtx.surfaceless { return errors.New("no surface created yet EGL_KHR_surfaceless_context is not supported") } if !eglMakeCurrent(c.disp, c.eglSurf, c.eglSurf, c.eglCtx.ctx) { return fmt.Errorf("eglMakeCurrent error 0x%x", eglGetError()) } - if c.eglCtx.srgb || c.eglSurf == nilEGLSurface { - return nil - } - if c.srgbFBO == nil { - var err error - c.srgbFBO, err = srgb.New(c.c) - if err != nil { - return err - } - } - if c.refreshFBO { - c.refreshFBO = false - return c.srgbFBO.Refresh(c.width, c.height) - } return nil } @@ -216,11 +185,9 @@ func createContext(disp _EGLDisplay) (*eglContext, error) { // Some Mesa drivers crash if an sRGB framebuffer is requested without alpha. // https://bugs.freedesktop.org/show_bug.cgi?id=107782. // - // Also, some Android devices (Samsung S9) needs alpha for sRGB to work. - attribs = append(attribs, _EGL_ALPHA_SIZE, 1) + // Also, some Android devices (Samsung S9) need alpha for sRGB to work. + attribs = append(attribs, _EGL_ALPHA_SIZE, 8) } - // Only request a depth buffer if we're going to render directly to the framebuffer. - attribs = append(attribs, _EGL_DEPTH_SIZE, 16) } attribs = append(attribs, _EGL_NONE) eglCfg, ret := eglChooseConfig(disp, attribs) @@ -228,11 +195,18 @@ func createContext(disp _EGLDisplay) (*eglContext, error) { return nil, fmt.Errorf("eglChooseConfig failed: 0x%x", eglGetError()) } if eglCfg == nilEGLConfig { - return nil, errors.New("eglChooseConfig returned 0 configs") + supportsNoCfg := hasExtension(exts, "EGL_KHR_no_config_context") + if !supportsNoCfg { + return nil, errors.New("eglChooseConfig returned no configs") + } } - visID, ret := eglGetConfigAttrib(disp, eglCfg, _EGL_NATIVE_VISUAL_ID) - if !ret { - return nil, errors.New("newContext: eglGetConfigAttrib for _EGL_NATIVE_VISUAL_ID failed") + var visID _EGLint + if eglCfg != nilEGLConfig { + var ok bool + visID, ok = eglGetConfigAttrib(disp, eglCfg, _EGL_NATIVE_VISUAL_ID) + if !ok { + return nil, errors.New("newContext: eglGetConfigAttrib for _EGL_NATIVE_VISUAL_ID failed") + } } ctxAttribs := []_EGLint{ _EGL_CONTEXT_CLIENT_VERSION, 3, @@ -267,7 +241,7 @@ func createSurface(disp _EGLDisplay, eglCtx *eglContext, win NativeWindowType) ( surfAttribs = append(surfAttribs, _EGL_NONE) eglSurf := eglCreateWindowSurface(disp, eglCtx.config, win, surfAttribs) if eglSurf == nilEGLSurface && eglCtx.srgb { - // Try again without sRGB + // Try again without sRGB. eglCtx.srgb = false surfAttribs = []_EGLint{_EGL_NONE} eglSurf = eglCreateWindowSurface(disp, eglCtx.config, win, surfAttribs) diff --git a/vendor/gioui.org/app/internal/egl/egl_unix.go b/vendor/gioui.org/internal/egl/egl_unix.go index 0c6734f..bd3efa5 100644 --- a/vendor/gioui.org/app/internal/egl/egl_unix.go +++ b/vendor/gioui.org/internal/egl/egl_unix.go @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Unlicense OR MIT +//go:build linux || freebsd || openbsd // +build linux freebsd openbsd package egl @@ -11,7 +12,7 @@ package egl #cgo freebsd LDFLAGS: -L/usr/local/lib #cgo openbsd CFLAGS: -I/usr/X11R6/include #cgo openbsd LDFLAGS: -L/usr/X11R6/lib -#cgo CFLAGS: -DMESA_EGL_NO_X11_HEADERS +#cgo CFLAGS: -DEGL_NO_X11 #include <EGL/egl.h> #include <EGL/eglext.h> @@ -102,3 +103,7 @@ func eglCreateWindowSurface(disp _EGLDisplay, conf _EGLConfig, win NativeWindowT eglSurf := C.eglCreateWindowSurface(disp, conf, win, &attribs[0]) return eglSurf } + +func eglWaitClient() bool { + return C.eglWaitClient() == C.EGL_TRUE +} diff --git a/vendor/gioui.org/app/internal/egl/egl_windows.go b/vendor/gioui.org/internal/egl/egl_windows.go index 5d9a4fe..4433dd7 100644 --- a/vendor/gioui.org/app/internal/egl/egl_windows.go +++ b/vendor/gioui.org/internal/egl/egl_windows.go @@ -10,8 +10,7 @@ import ( syscall "golang.org/x/sys/windows" - "gioui.org/app/internal/glimpl" - gunsafe "gioui.org/internal/unsafe" + "gioui.org/internal/gl" ) type ( @@ -41,6 +40,7 @@ var ( _eglSwapBuffers = libEGL.NewProc("eglSwapBuffers") _eglTerminate = libEGL.NewProc("eglTerminate") _eglQueryString = libEGL.NewProc("eglQueryString") + _eglWaitClient = libEGL.NewProc("eglWaitClient") ) var loadOnce sync.Once @@ -57,7 +57,7 @@ func loadDLLs() error { if err := loadDLL(libEGL, "libEGL.dll"); err != nil { return err } - if err := loadDLL(glimpl.LibGLESv2, "libGLESv2.dll"); err != nil { + if err := loadDLL(gl.LibGLESv2, "libGLESv2.dll"); err != nil { return err } // d3dcompiler_47.dll is needed internally for shader compilation to function. @@ -154,7 +154,12 @@ func eglTerminate(disp _EGLDisplay) bool { func eglQueryString(disp _EGLDisplay, name _EGLint) string { r, _, _ := _eglQueryString.Call(uintptr(disp), uintptr(name)) - return gunsafe.GoString(gunsafe.SliceOf(r)) + return syscall.BytePtrToString((*byte)(unsafe.Pointer(r))) +} + +func eglWaitClient() bool { + r, _, _ := _eglWaitClient.Call() + return r != 0 } // issue34474KeepAlive calls runtime.KeepAlive as a diff --git a/vendor/gioui.org/internal/f32color/rgba.go b/vendor/gioui.org/internal/f32color/rgba.go index efaa1fb..eecf018 100644 --- a/vendor/gioui.org/internal/f32color/rgba.go +++ b/vendor/gioui.org/internal/f32color/rgba.go @@ -7,7 +7,7 @@ import ( "math" ) -// RGBA is a 32 bit floating point linear space color. +// RGBA is a 32 bit floating point linear premultiplied color space. type RGBA struct { R, G, B, A float32 } @@ -23,30 +23,90 @@ func (col RGBA) Float32() (r, g, b, a float32) { } // SRGBA converts from linear to sRGB color space. -func (col RGBA) SRGB() color.RGBA { - return color.RGBA{ - R: uint8(linearTosRGB(col.R)*255 + .5), - G: uint8(linearTosRGB(col.G)*255 + .5), - B: uint8(linearTosRGB(col.B)*255 + .5), +func (col RGBA) SRGB() color.NRGBA { + if col.A == 0 { + return color.NRGBA{} + } + return color.NRGBA{ + R: uint8(linearTosRGB(col.R/col.A)*255 + .5), + G: uint8(linearTosRGB(col.G/col.A)*255 + .5), + B: uint8(linearTosRGB(col.B/col.A)*255 + .5), A: uint8(col.A*255 + .5), } } +// Luminance calculates the relative luminance of a linear RGBA color. +// Normalized to 0 for black and 1 for white. +// +// See https://www.w3.org/TR/WCAG20/#relativeluminancedef for more details +func (col RGBA) Luminance() float32 { + return 0.2126*col.R + 0.7152*col.G + 0.0722*col.B +} + // Opaque returns the color without alpha component. func (col RGBA) Opaque() RGBA { col.A = 1.0 return col } -// RGBAFromSRGB converts from SRGBA to RGBA. -func RGBAFromSRGB(col color.RGBA) RGBA { - r, g, b, a := col.RGBA() +// LinearFromSRGB converts from col in the sRGB colorspace to RGBA. +func LinearFromSRGB(col color.NRGBA) RGBA { + af := float32(col.A) / 0xFF return RGBA{ - R: sRGBToLinear(float32(r) / 0xffff), - G: sRGBToLinear(float32(g) / 0xffff), - B: sRGBToLinear(float32(b) / 0xffff), - A: float32(a) / 0xFFFF, + R: sRGBToLinear(float32(col.R)/0xff) * af, + G: sRGBToLinear(float32(col.G)/0xff) * af, + B: sRGBToLinear(float32(col.B)/0xff) * af, + A: af, + } +} + +// NRGBAToRGBA converts from non-premultiplied sRGB color to premultiplied sRGB color. +// +// Each component in the result is `sRGBToLinear(c * alpha)`, where `c` +// is the linear color. +func NRGBAToRGBA(col color.NRGBA) color.RGBA { + if col.A == 0xFF { + return color.RGBA(col) + } + c := LinearFromSRGB(col) + return color.RGBA{ + R: uint8(linearTosRGB(c.R)*255 + .5), + G: uint8(linearTosRGB(c.G)*255 + .5), + B: uint8(linearTosRGB(c.B)*255 + .5), + A: col.A, + } +} + +// NRGBAToLinearRGBA converts from non-premultiplied sRGB color to premultiplied linear RGBA color. +// +// Each component in the result is `c * alpha`, where `c` is the linear color. +func NRGBAToLinearRGBA(col color.NRGBA) color.RGBA { + if col.A == 0xFF { + return color.RGBA(col) + } + c := LinearFromSRGB(col) + return color.RGBA{ + R: uint8(c.R*255 + .5), + G: uint8(c.G*255 + .5), + B: uint8(c.B*255 + .5), + A: col.A, + } +} + +// RGBAToNRGBA converts from premultiplied sRGB color to non-premultiplied sRGB color. +func RGBAToNRGBA(col color.RGBA) color.NRGBA { + if col.A == 0xFF { + return color.NRGBA(col) + } + + linear := RGBA{ + R: sRGBToLinear(float32(col.R) / 0xff), + G: sRGBToLinear(float32(col.G) / 0xff), + B: sRGBToLinear(float32(col.B) / 0xff), + A: float32(col.A) / 0xff, } + + return linear.SRGB() } // linearTosRGB transforms color value from linear to sRGB. @@ -73,3 +133,45 @@ func sRGBToLinear(c float32) float32 { return float32(math.Pow(float64((c+0.055)/1.055), 2.4)) } } + +// MulAlpha applies the alpha to the color. +func MulAlpha(c color.NRGBA, alpha uint8) color.NRGBA { + c.A = uint8(uint32(c.A) * uint32(alpha) / 0xFF) + return c +} + +// Disabled blends color towards the luminance and multiplies alpha. +// Blending towards luminance will desaturate the color. +// Multiplying alpha blends the color together more with the background. +func Disabled(c color.NRGBA) (d color.NRGBA) { + const r = 80 // blend ratio + lum := approxLuminance(c) + return color.NRGBA{ + R: byte((int(c.R)*r + int(lum)*(256-r)) / 256), + G: byte((int(c.G)*r + int(lum)*(256-r)) / 256), + B: byte((int(c.B)*r + int(lum)*(256-r)) / 256), + A: byte(int(c.A) * (128 + 32) / 256), + } +} + +// Hovered blends color towards a brighter color. +func Hovered(c color.NRGBA) (d color.NRGBA) { + const r = 0x20 // lighten ratio + return color.NRGBA{ + R: byte(255 - int(255-c.R)*(255-r)/256), + G: byte(255 - int(255-c.G)*(255-r)/256), + B: byte(255 - int(255-c.B)*(255-r)/256), + A: c.A, + } +} + +// approxLuminance is a fast approximate version of RGBA.Luminance. +func approxLuminance(c color.NRGBA) byte { + const ( + r = 13933 // 0.2126 * 256 * 256 + g = 46871 // 0.7152 * 256 * 256 + b = 4732 // 0.0722 * 256 * 256 + t = r + g + b + ) + return byte((r*int(c.R) + g*int(c.G) + b*int(c.B)) / t) +} diff --git a/vendor/gioui.org/gpu/gl/gl.go b/vendor/gioui.org/internal/gl/gl.go index 350f60f..a9e378a 100644 --- a/vendor/gioui.org/gpu/gl/gl.go +++ b/vendor/gioui.org/internal/gl/gl.go @@ -8,20 +8,38 @@ type ( ) const ( + ACTIVE_TEXTURE = 0x84E0 + ALL_BARRIER_BITS = 0xffffffff ARRAY_BUFFER = 0x8892 + ARRAY_BUFFER_BINDING = 0x8894 + BACK = 0x0405 BLEND = 0xbe2 + BLEND_DST_RGB = 0x80C8 + BLEND_SRC_RGB = 0x80C9 + BLEND_DST_ALPHA = 0x80CA + BLEND_SRC_ALPHA = 0x80CB CLAMP_TO_EDGE = 0x812f COLOR_ATTACHMENT0 = 0x8ce0 COLOR_BUFFER_BIT = 0x4000 + COLOR_CLEAR_VALUE = 0x0C22 COMPILE_STATUS = 0x8b81 - DEPTH_BUFFER_BIT = 0x100 + COMPUTE_SHADER = 0x91B9 + CURRENT_PROGRAM = 0x8B8D DEPTH_ATTACHMENT = 0x8d00 + DEPTH_BUFFER_BIT = 0x100 + DEPTH_CLEAR_VALUE = 0x0B73 DEPTH_COMPONENT16 = 0x81a5 DEPTH_COMPONENT24 = 0x81A6 DEPTH_COMPONENT32F = 0x8CAC + DEPTH_FUNC = 0x0B74 DEPTH_TEST = 0xb71 + DEPTH_WRITEMASK = 0x0B72 + DRAW_FRAMEBUFFER = 0x8CA9 DST_COLOR = 0x306 + DYNAMIC_DRAW = 0x88E8 + DYNAMIC_READ = 0x88E9 ELEMENT_ARRAY_BUFFER = 0x8893 + ELEMENT_ARRAY_BUFFER_BINDING = 0x8895 EXTENSIONS = 0x1f03 FALSE = 0 FLOAT = 0x1406 @@ -30,6 +48,7 @@ const ( FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING = 0x8210 FRAMEBUFFER_BINDING = 0x8ca6 FRAMEBUFFER_COMPLETE = 0x8cd5 + FRAMEBUFFER_SRGB = 0x8db9 HALF_FLOAT = 0x140b HALF_FLOAT_OES = 0x8d61 INFO_LOG_LENGTH = 0x8B84 @@ -39,17 +58,23 @@ const ( LINEAR = 0x2601 LINK_STATUS = 0x8b82 LUMINANCE = 0x1909 + MAP_READ_BIT = 0x0001 MAX_TEXTURE_SIZE = 0xd33 NEAREST = 0x2600 NO_ERROR = 0x0 NUM_EXTENSIONS = 0x821D ONE = 0x1 ONE_MINUS_SRC_ALPHA = 0x303 + PACK_ROW_LENGTH = 0x0D02 + PROGRAM_BINARY_LENGTH = 0x8741 QUERY_RESULT = 0x8866 QUERY_RESULT_AVAILABLE = 0x8867 R16F = 0x822d R8 = 0x8229 READ_FRAMEBUFFER = 0x8ca8 + READ_FRAMEBUFFER_BINDING = 0x8CAA + READ_ONLY = 0x88B8 + READ_WRITE = 0x88BA RED = 0x1903 RENDERER = 0x1F01 RENDERBUFFER = 0x8d41 @@ -59,13 +84,17 @@ const ( RGB = 0x1907 RGBA = 0x1908 RGBA8 = 0x8058 + SHADER_STORAGE_BUFFER = 0x90D2 + SHADER_STORAGE_BUFFER_BINDING = 0x90D3 SHORT = 0x1402 SRGB = 0x8c40 SRGB_ALPHA_EXT = 0x8c42 SRGB8 = 0x8c41 SRGB8_ALPHA8 = 0x8c43 STATIC_DRAW = 0x88e4 + STENCIL_BUFFER_BIT = 0x00000400 TEXTURE_2D = 0xde1 + TEXTURE_BINDING_2D = 0x8069 TEXTURE_MAG_FILTER = 0x2800 TEXTURE_MIN_FILTER = 0x2801 TEXTURE_WRAP_S = 0x2802 @@ -76,86 +105,26 @@ const ( TRIANGLES = 0x4 TRUE = 1 UNIFORM_BUFFER = 0x8A11 + UNIFORM_BUFFER_BINDING = 0x8A28 UNPACK_ALIGNMENT = 0xcf5 + UNPACK_ROW_LENGTH = 0x0CF2 UNSIGNED_BYTE = 0x1401 UNSIGNED_SHORT = 0x1403 + VIEWPORT = 0x0BA2 VERSION = 0x1f02 + VERTEX_ARRAY_BINDING = 0x85B5 VERTEX_SHADER = 0x8b31 + VERTEX_ATTRIB_ARRAY_BUFFER_BINDING = 0x889F + VERTEX_ATTRIB_ARRAY_ENABLED = 0x8622 + VERTEX_ATTRIB_ARRAY_POINTER = 0x8645 + VERTEX_ATTRIB_ARRAY_NORMALIZED = 0x886A + VERTEX_ATTRIB_ARRAY_SIZE = 0x8623 + VERTEX_ATTRIB_ARRAY_STRIDE = 0x8624 + VERTEX_ATTRIB_ARRAY_TYPE = 0x8625 + WRITE_ONLY = 0x88B9 ZERO = 0x0 // EXT_disjoint_timer_query TIME_ELAPSED_EXT = 0x88BF GPU_DISJOINT_EXT = 0x8FBB ) - -type Functions interface { - ActiveTexture(texture Enum) - AttachShader(p Program, s Shader) - BeginQuery(target Enum, query Query) - BindAttribLocation(p Program, a Attrib, name string) - BindBuffer(target Enum, b Buffer) - BindBufferBase(target Enum, index int, buffer Buffer) - BindFramebuffer(target Enum, fb Framebuffer) - BindRenderbuffer(target Enum, fb Renderbuffer) - BindTexture(target Enum, t Texture) - BlendEquation(mode Enum) - BlendFunc(sfactor, dfactor Enum) - BufferData(target Enum, src []byte, usage Enum) - CheckFramebufferStatus(target Enum) Enum - Clear(mask Enum) - ClearColor(red, green, blue, alpha float32) - ClearDepthf(d float32) - CompileShader(s Shader) - CreateBuffer() Buffer - CreateFramebuffer() Framebuffer - CreateProgram() Program - CreateQuery() Query - CreateRenderbuffer() Renderbuffer - CreateShader(ty Enum) Shader - CreateTexture() Texture - DeleteBuffer(v Buffer) - DeleteFramebuffer(v Framebuffer) - DeleteProgram(p Program) - DeleteQuery(query Query) - DeleteRenderbuffer(r Renderbuffer) - DeleteShader(s Shader) - DeleteTexture(v Texture) - DepthFunc(f Enum) - DepthMask(mask bool) - DisableVertexAttribArray(a Attrib) - Disable(cap Enum) - DrawArrays(mode Enum, first, count int) - DrawElements(mode Enum, count int, ty Enum, offset int) - Enable(cap Enum) - EnableVertexAttribArray(a Attrib) - EndQuery(target Enum) - FramebufferTexture2D(target, attachment, texTarget Enum, t Texture, level int) - FramebufferRenderbuffer(target, attachment, renderbuffertarget Enum, renderbuffer Renderbuffer) - GetBinding(pname Enum) Object - GetError() Enum - GetInteger(pname Enum) int - GetProgrami(p Program, pname Enum) int - GetProgramInfoLog(p Program) string - GetQueryObjectuiv(query Query, pname Enum) uint - GetShaderi(s Shader, pname Enum) int - GetShaderInfoLog(s Shader) string - GetString(pname Enum) string - GetUniformBlockIndex(p Program, name string) uint - GetUniformLocation(p Program, name string) Uniform - InvalidateFramebuffer(target, attachment Enum) - LinkProgram(p Program) - ReadPixels(x, y, width, height int, format, ty Enum, data []byte) - RenderbufferStorage(target, internalformat Enum, width, height int) - ShaderSource(s Shader, src string) - TexImage2D(target Enum, level int, internalFormat int, width, height int, format, ty Enum, data []byte) - TexParameteri(target, pname Enum, param int) - UniformBlockBinding(p Program, uniformBlockIndex uint, uniformBlockBinding uint) - Uniform1f(dst Uniform, v float32) - Uniform1i(dst Uniform, v int) - Uniform2f(dst Uniform, v0, v1 float32) - Uniform3f(dst Uniform, v0, v1, v2 float32) - Uniform4f(dst Uniform, v0, v1, v2, v3 float32) - UseProgram(p Program) - VertexAttribPointer(dst Attrib, size int, ty Enum, normalized bool, stride, offset int) - Viewport(x, y, width, height int) -} diff --git a/vendor/gioui.org/internal/gl/gl_js.go b/vendor/gioui.org/internal/gl/gl_js.go new file mode 100644 index 0000000..2c7976e --- /dev/null +++ b/vendor/gioui.org/internal/gl/gl_js.go @@ -0,0 +1,458 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package gl + +import ( + "errors" + "strings" + "syscall/js" +) + +type Functions struct { + Ctx js.Value + EXT_disjoint_timer_query js.Value + EXT_disjoint_timer_query_webgl2 js.Value + + // Cached reference to the Uint8Array JS type. + uint8Array js.Value + + // Cached JS arrays. + arrayBuf js.Value + int32Buf js.Value + + isWebGL2 bool +} + +type Context js.Value + +func NewFunctions(ctx Context, forceES bool) (*Functions, error) { + f := &Functions{ + Ctx: js.Value(ctx), + uint8Array: js.Global().Get("Uint8Array"), + } + if err := f.Init(); err != nil { + return nil, err + } + return f, nil +} + +func (f *Functions) Init() error { + webgl2Class := js.Global().Get("WebGL2RenderingContext") + f.isWebGL2 = !webgl2Class.IsUndefined() && f.Ctx.InstanceOf(webgl2Class) + if !f.isWebGL2 { + f.EXT_disjoint_timer_query = f.getExtension("EXT_disjoint_timer_query") + if f.getExtension("OES_texture_half_float").IsNull() && f.getExtension("OES_texture_float").IsNull() { + return errors.New("gl: no support for neither OES_texture_half_float nor OES_texture_float") + } + if f.getExtension("EXT_sRGB").IsNull() { + return errors.New("gl: EXT_sRGB not supported") + } + } else { + // WebGL2 extensions. + f.EXT_disjoint_timer_query_webgl2 = f.getExtension("EXT_disjoint_timer_query_webgl2") + if f.getExtension("EXT_color_buffer_half_float").IsNull() && f.getExtension("EXT_color_buffer_float").IsNull() { + return errors.New("gl: no support for neither EXT_color_buffer_half_float nor EXT_color_buffer_float") + } + } + return nil +} + +func (f *Functions) getExtension(name string) js.Value { + return f.Ctx.Call("getExtension", name) +} + +func (f *Functions) ActiveTexture(t Enum) { + f.Ctx.Call("activeTexture", int(t)) +} +func (f *Functions) AttachShader(p Program, s Shader) { + f.Ctx.Call("attachShader", js.Value(p), js.Value(s)) +} +func (f *Functions) BeginQuery(target Enum, query Query) { + if !f.EXT_disjoint_timer_query_webgl2.IsNull() { + f.Ctx.Call("beginQuery", int(target), js.Value(query)) + } else { + f.EXT_disjoint_timer_query.Call("beginQueryEXT", int(target), js.Value(query)) + } +} +func (f *Functions) BindAttribLocation(p Program, a Attrib, name string) { + f.Ctx.Call("bindAttribLocation", js.Value(p), int(a), name) +} +func (f *Functions) BindBuffer(target Enum, b Buffer) { + f.Ctx.Call("bindBuffer", int(target), js.Value(b)) +} +func (f *Functions) BindBufferBase(target Enum, index int, b Buffer) { + f.Ctx.Call("bindBufferBase", int(target), index, js.Value(b)) +} +func (f *Functions) BindFramebuffer(target Enum, fb Framebuffer) { + f.Ctx.Call("bindFramebuffer", int(target), js.Value(fb)) +} +func (f *Functions) BindRenderbuffer(target Enum, rb Renderbuffer) { + f.Ctx.Call("bindRenderbuffer", int(target), js.Value(rb)) +} +func (f *Functions) BindTexture(target Enum, t Texture) { + f.Ctx.Call("bindTexture", int(target), js.Value(t)) +} +func (f *Functions) BindImageTexture(unit int, t Texture, level int, layered bool, layer int, access, format Enum) { + panic("not implemented") +} +func (f *Functions) BindVertexArray(a VertexArray) { + panic("not supported") +} +func (f *Functions) BlendEquation(mode Enum) { + f.Ctx.Call("blendEquation", int(mode)) +} +func (f *Functions) BlendFuncSeparate(srcRGB, dstRGB, srcA, dstA Enum) { + f.Ctx.Call("blendFunc", int(srcRGB), int(dstRGB), int(srcA), int(dstA)) +} +func (f *Functions) BufferData(target Enum, size int, usage Enum, data []byte) { + if data == nil { + f.Ctx.Call("bufferData", int(target), size, int(usage)) + } else { + if len(data) != size { + panic("size mismatch") + } + f.Ctx.Call("bufferData", int(target), f.byteArrayOf(data), int(usage)) + } +} +func (f *Functions) BufferSubData(target Enum, offset int, src []byte) { + f.Ctx.Call("bufferSubData", int(target), offset, f.byteArrayOf(src)) +} +func (f *Functions) CheckFramebufferStatus(target Enum) Enum { + return Enum(f.Ctx.Call("checkFramebufferStatus", int(target)).Int()) +} +func (f *Functions) Clear(mask Enum) { + f.Ctx.Call("clear", int(mask)) +} +func (f *Functions) ClearColor(red, green, blue, alpha float32) { + f.Ctx.Call("clearColor", red, green, blue, alpha) +} +func (f *Functions) ClearDepthf(d float32) { + f.Ctx.Call("clearDepth", d) +} +func (f *Functions) CompileShader(s Shader) { + f.Ctx.Call("compileShader", js.Value(s)) +} +func (f *Functions) CopyTexSubImage2D(target Enum, level, xoffset, yoffset, x, y, width, height int) { + f.Ctx.Call("copyTexSubImage2D", int(target), level, xoffset, yoffset, x, y, width, height) +} +func (f *Functions) CreateBuffer() Buffer { + return Buffer(f.Ctx.Call("createBuffer")) +} +func (f *Functions) CreateFramebuffer() Framebuffer { + return Framebuffer(f.Ctx.Call("createFramebuffer")) +} +func (f *Functions) CreateProgram() Program { + return Program(f.Ctx.Call("createProgram")) +} +func (f *Functions) CreateQuery() Query { + return Query(f.Ctx.Call("createQuery")) +} +func (f *Functions) CreateRenderbuffer() Renderbuffer { + return Renderbuffer(f.Ctx.Call("createRenderbuffer")) +} +func (f *Functions) CreateShader(ty Enum) Shader { + return Shader(f.Ctx.Call("createShader", int(ty))) +} +func (f *Functions) CreateTexture() Texture { + return Texture(f.Ctx.Call("createTexture")) +} +func (f *Functions) CreateVertexArray() VertexArray { + panic("not supported") +} +func (f *Functions) DeleteBuffer(v Buffer) { + f.Ctx.Call("deleteBuffer", js.Value(v)) +} +func (f *Functions) DeleteFramebuffer(v Framebuffer) { + f.Ctx.Call("deleteFramebuffer", js.Value(v)) +} +func (f *Functions) DeleteProgram(p Program) { + f.Ctx.Call("deleteProgram", js.Value(p)) +} +func (f *Functions) DeleteQuery(query Query) { + if !f.EXT_disjoint_timer_query_webgl2.IsNull() { + f.Ctx.Call("deleteQuery", js.Value(query)) + } else { + f.EXT_disjoint_timer_query.Call("deleteQueryEXT", js.Value(query)) + } +} +func (f *Functions) DeleteShader(s Shader) { + f.Ctx.Call("deleteShader", js.Value(s)) +} +func (f *Functions) DeleteRenderbuffer(v Renderbuffer) { + f.Ctx.Call("deleteRenderbuffer", js.Value(v)) +} +func (f *Functions) DeleteTexture(v Texture) { + f.Ctx.Call("deleteTexture", js.Value(v)) +} +func (f *Functions) DeleteVertexArray(a VertexArray) { + panic("not implemented") +} +func (f *Functions) DepthFunc(fn Enum) { + f.Ctx.Call("depthFunc", int(fn)) +} +func (f *Functions) DepthMask(mask bool) { + f.Ctx.Call("depthMask", mask) +} +func (f *Functions) DisableVertexAttribArray(a Attrib) { + f.Ctx.Call("disableVertexAttribArray", int(a)) +} +func (f *Functions) Disable(cap Enum) { + f.Ctx.Call("disable", int(cap)) +} +func (f *Functions) DrawArrays(mode Enum, first, count int) { + f.Ctx.Call("drawArrays", int(mode), first, count) +} +func (f *Functions) DrawElements(mode Enum, count int, ty Enum, offset int) { + f.Ctx.Call("drawElements", int(mode), count, int(ty), offset) +} +func (f *Functions) DispatchCompute(x, y, z int) { + panic("not implemented") +} +func (f *Functions) Enable(cap Enum) { + f.Ctx.Call("enable", int(cap)) +} +func (f *Functions) EnableVertexAttribArray(a Attrib) { + f.Ctx.Call("enableVertexAttribArray", int(a)) +} +func (f *Functions) EndQuery(target Enum) { + if !f.EXT_disjoint_timer_query_webgl2.IsNull() { + f.Ctx.Call("endQuery", int(target)) + } else { + f.EXT_disjoint_timer_query.Call("endQueryEXT", int(target)) + } +} +func (f *Functions) Finish() { + f.Ctx.Call("finish") +} +func (f *Functions) Flush() { + f.Ctx.Call("flush") +} +func (f *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget Enum, renderbuffer Renderbuffer) { + f.Ctx.Call("framebufferRenderbuffer", int(target), int(attachment), int(renderbuffertarget), js.Value(renderbuffer)) +} +func (f *Functions) FramebufferTexture2D(target, attachment, texTarget Enum, t Texture, level int) { + f.Ctx.Call("framebufferTexture2D", int(target), int(attachment), int(texTarget), js.Value(t), level) +} +func (f *Functions) GetError() Enum { + // Avoid slow getError calls. See gio#179. + return 0 +} +func (f *Functions) GetRenderbufferParameteri(target, pname Enum) int { + return paramVal(f.Ctx.Call("getRenderbufferParameteri", int(pname))) +} +func (f *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname Enum) int { + if !f.isWebGL2 && pname == FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING { + // FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING is only available on WebGL 2 + return LINEAR + } + return paramVal(f.Ctx.Call("getFramebufferAttachmentParameter", int(target), int(attachment), int(pname))) +} +func (f *Functions) GetBinding(pname Enum) Object { + obj := f.Ctx.Call("getParameter", int(pname)) + if !obj.Truthy() { + return Object{} + } + return Object(obj) +} +func (f *Functions) GetBindingi(pname Enum, idx int) Object { + obj := f.Ctx.Call("getIndexedParameter", int(pname), idx) + if !obj.Truthy() { + return Object{} + } + return Object(obj) +} +func (f *Functions) GetInteger(pname Enum) int { + if !f.isWebGL2 { + switch pname { + case PACK_ROW_LENGTH, UNPACK_ROW_LENGTH: + return 0 // PACK_ROW_LENGTH and UNPACK_ROW_LENGTH is only available on WebGL 2 + } + } + return paramVal(f.Ctx.Call("getParameter", int(pname))) +} +func (f *Functions) GetFloat(pname Enum) float32 { + return float32(f.Ctx.Call("getParameter", int(pname)).Float()) +} +func (f *Functions) GetInteger4(pname Enum) [4]int { + arr := f.Ctx.Call("getParameter", int(pname)) + var res [4]int + for i := range res { + res[i] = arr.Index(i).Int() + } + return res +} +func (f *Functions) GetFloat4(pname Enum) [4]float32 { + arr := f.Ctx.Call("getParameter", int(pname)) + var res [4]float32 + for i := range res { + res[i] = float32(arr.Index(i).Float()) + } + return res +} +func (f *Functions) GetProgrami(p Program, pname Enum) int { + return paramVal(f.Ctx.Call("getProgramParameter", js.Value(p), int(pname))) +} +func (f *Functions) GetProgramInfoLog(p Program) string { + return f.Ctx.Call("getProgramInfoLog", js.Value(p)).String() +} +func (f *Functions) GetQueryObjectuiv(query Query, pname Enum) uint { + if !f.EXT_disjoint_timer_query_webgl2.IsNull() { + return uint(paramVal(f.Ctx.Call("getQueryParameter", js.Value(query), int(pname)))) + } else { + return uint(paramVal(f.EXT_disjoint_timer_query.Call("getQueryObjectEXT", js.Value(query), int(pname)))) + } +} +func (f *Functions) GetShaderi(s Shader, pname Enum) int { + return paramVal(f.Ctx.Call("getShaderParameter", js.Value(s), int(pname))) +} +func (f *Functions) GetShaderInfoLog(s Shader) string { + return f.Ctx.Call("getShaderInfoLog", js.Value(s)).String() +} +func (f *Functions) GetString(pname Enum) string { + switch pname { + case EXTENSIONS: + extsjs := f.Ctx.Call("getSupportedExtensions") + var exts []string + for i := 0; i < extsjs.Length(); i++ { + exts = append(exts, "GL_"+extsjs.Index(i).String()) + } + return strings.Join(exts, " ") + default: + return f.Ctx.Call("getParameter", int(pname)).String() + } +} +func (f *Functions) GetUniformBlockIndex(p Program, name string) uint { + return uint(paramVal(f.Ctx.Call("getUniformBlockIndex", js.Value(p), name))) +} +func (f *Functions) GetUniformLocation(p Program, name string) Uniform { + return Uniform(f.Ctx.Call("getUniformLocation", js.Value(p), name)) +} +func (f *Functions) GetVertexAttrib(index int, pname Enum) int { + return paramVal(f.Ctx.Call("getVertexAttrib", index, int(pname))) +} +func (f *Functions) GetVertexAttribBinding(index int, pname Enum) Object { + obj := f.Ctx.Call("getVertexAttrib", index, int(pname)) + if !obj.Truthy() { + return Object{} + } + return Object(obj) +} +func (f *Functions) GetVertexAttribPointer(index int, pname Enum) uintptr { + return uintptr(f.Ctx.Call("getVertexAttribOffset", index, int(pname)).Int()) +} +func (f *Functions) InvalidateFramebuffer(target, attachment Enum) { + fn := f.Ctx.Get("invalidateFramebuffer") + if !fn.IsUndefined() { + if f.int32Buf.IsUndefined() { + f.int32Buf = js.Global().Get("Int32Array").New(1) + } + f.int32Buf.SetIndex(0, int32(attachment)) + f.Ctx.Call("invalidateFramebuffer", int(target), f.int32Buf) + } +} +func (f *Functions) IsEnabled(cap Enum) bool { + return f.Ctx.Call("isEnabled", int(cap)).Truthy() +} +func (f *Functions) LinkProgram(p Program) { + f.Ctx.Call("linkProgram", js.Value(p)) +} +func (f *Functions) PixelStorei(pname Enum, param int) { + f.Ctx.Call("pixelStorei", int(pname), param) +} +func (f *Functions) MemoryBarrier(barriers Enum) { + panic("not implemented") +} +func (f *Functions) MapBufferRange(target Enum, offset, length int, access Enum) []byte { + panic("not implemented") +} +func (f *Functions) RenderbufferStorage(target, internalformat Enum, width, height int) { + f.Ctx.Call("renderbufferStorage", int(target), int(internalformat), width, height) +} +func (f *Functions) ReadPixels(x, y, width, height int, format, ty Enum, data []byte) { + ba := f.byteArrayOf(data) + f.Ctx.Call("readPixels", x, y, width, height, int(format), int(ty), ba) + js.CopyBytesToGo(data, ba) +} +func (f *Functions) Scissor(x, y, width, height int32) { + f.Ctx.Call("scissor", x, y, width, height) +} +func (f *Functions) ShaderSource(s Shader, src string) { + f.Ctx.Call("shaderSource", js.Value(s), src) +} +func (f *Functions) TexImage2D(target Enum, level int, internalFormat Enum, width, height int, format, ty Enum) { + f.Ctx.Call("texImage2D", int(target), int(level), int(internalFormat), int(width), int(height), 0, int(format), int(ty), nil) +} +func (f *Functions) TexStorage2D(target Enum, levels int, internalFormat Enum, width, height int) { + f.Ctx.Call("texStorage2D", int(target), levels, int(internalFormat), width, height) +} +func (f *Functions) TexSubImage2D(target Enum, level int, x, y, width, height int, format, ty Enum, data []byte) { + f.Ctx.Call("texSubImage2D", int(target), level, x, y, width, height, int(format), int(ty), f.byteArrayOf(data)) +} +func (f *Functions) TexParameteri(target, pname Enum, param int) { + f.Ctx.Call("texParameteri", int(target), int(pname), int(param)) +} +func (f *Functions) UniformBlockBinding(p Program, uniformBlockIndex uint, uniformBlockBinding uint) { + f.Ctx.Call("uniformBlockBinding", js.Value(p), int(uniformBlockIndex), int(uniformBlockBinding)) +} +func (f *Functions) Uniform1f(dst Uniform, v float32) { + f.Ctx.Call("uniform1f", js.Value(dst), v) +} +func (f *Functions) Uniform1i(dst Uniform, v int) { + f.Ctx.Call("uniform1i", js.Value(dst), v) +} +func (f *Functions) Uniform2f(dst Uniform, v0, v1 float32) { + f.Ctx.Call("uniform2f", js.Value(dst), v0, v1) +} +func (f *Functions) Uniform3f(dst Uniform, v0, v1, v2 float32) { + f.Ctx.Call("uniform3f", js.Value(dst), v0, v1, v2) +} +func (f *Functions) Uniform4f(dst Uniform, v0, v1, v2, v3 float32) { + f.Ctx.Call("uniform4f", js.Value(dst), v0, v1, v2, v3) +} +func (f *Functions) UseProgram(p Program) { + f.Ctx.Call("useProgram", js.Value(p)) +} +func (f *Functions) UnmapBuffer(target Enum) bool { + panic("not implemented") +} +func (f *Functions) VertexAttribPointer(dst Attrib, size int, ty Enum, normalized bool, stride, offset int) { + f.Ctx.Call("vertexAttribPointer", int(dst), size, int(ty), normalized, stride, offset) +} +func (f *Functions) Viewport(x, y, width, height int) { + f.Ctx.Call("viewport", x, y, width, height) +} + +func (f *Functions) byteArrayOf(data []byte) js.Value { + if len(data) == 0 { + return js.Null() + } + f.resizeByteBuffer(len(data)) + ba := f.uint8Array.New(f.arrayBuf, int(0), int(len(data))) + js.CopyBytesToJS(ba, data) + return ba +} + +func (f *Functions) resizeByteBuffer(n int) { + if n == 0 { + return + } + if !f.arrayBuf.IsUndefined() && f.arrayBuf.Length() >= n { + return + } + f.arrayBuf = js.Global().Get("ArrayBuffer").New(n) +} + +func paramVal(v js.Value) int { + switch v.Type() { + case js.TypeBoolean: + if b := v.Bool(); b { + return 1 + } else { + return 0 + } + case js.TypeNumber: + return v.Int() + default: + panic("unknown parameter type") + } +} diff --git a/vendor/gioui.org/internal/gl/gl_unix.go b/vendor/gioui.org/internal/gl/gl_unix.go new file mode 100644 index 0000000..f223ced --- /dev/null +++ b/vendor/gioui.org/internal/gl/gl_unix.go @@ -0,0 +1,1222 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build darwin || linux || freebsd || openbsd +// +build darwin linux freebsd openbsd + +package gl + +import ( + "fmt" + "runtime" + "strings" + "unsafe" +) + +/* +#cgo CFLAGS: -Werror +#cgo linux freebsd LDFLAGS: -ldl + +#include <stdint.h> +#include <stdlib.h> +#include <sys/types.h> +#define __USE_GNU +#include <dlfcn.h> + +typedef unsigned int GLenum; +typedef unsigned int GLuint; +typedef char GLchar; +typedef float GLfloat; +typedef ssize_t GLsizeiptr; +typedef intptr_t GLintptr; +typedef unsigned int GLbitfield; +typedef int GLint; +typedef unsigned char GLboolean; +typedef int GLsizei; +typedef uint8_t GLubyte; + +typedef struct { + void (*glActiveTexture)(GLenum texture); + void (*glAttachShader)(GLuint program, GLuint shader); + void (*glBindAttribLocation)(GLuint program, GLuint index, const GLchar *name); + void (*glBindBuffer)(GLenum target, GLuint buffer); + void (*glBindFramebuffer)(GLenum target, GLuint framebuffer); + void (*glBindRenderbuffer)(GLenum target, GLuint renderbuffer); + void (*glBindTexture)(GLenum target, GLuint texture); + void (*glBlendEquation)(GLenum mode); + void (*glBlendFuncSeparate)(GLenum srcRGB, GLenum dstRGB, GLenum srcA, GLenum dstA); + void (*glBufferData)(GLenum target, GLsizeiptr size, const void *data, GLenum usage); + void (*glBufferSubData)(GLenum target, GLintptr offset, GLsizeiptr size, const void *data); + GLenum (*glCheckFramebufferStatus)(GLenum target); + void (*glClear)(GLbitfield mask); + void (*glClearColor)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); + void (*glClearDepthf)(GLfloat d); + void (*glCompileShader)(GLuint shader); + void (*glCopyTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); + GLuint (*glCreateProgram)(void); + GLuint (*glCreateShader)(GLenum type); + void (*glDeleteBuffers)(GLsizei n, const GLuint *buffers); + void (*glDeleteFramebuffers)(GLsizei n, const GLuint *framebuffers); + void (*glDeleteProgram)(GLuint program); + void (*glDeleteRenderbuffers)(GLsizei n, const GLuint *renderbuffers); + void (*glDeleteShader)(GLuint shader); + void (*glDeleteTextures)(GLsizei n, const GLuint *textures); + void (*glDepthFunc)(GLenum func); + void (*glDepthMask)(GLboolean flag); + void (*glDisable)(GLenum cap); + void (*glDisableVertexAttribArray)(GLuint index); + void (*glDrawArrays)(GLenum mode, GLint first, GLsizei count); + void (*glDrawElements)(GLenum mode, GLsizei count, GLenum type, const void *indices); + void (*glEnable)(GLenum cap); + void (*glEnableVertexAttribArray)(GLuint index); + void (*glFinish)(void); + void (*glFlush)(void); + void (*glFramebufferRenderbuffer)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + void (*glFramebufferTexture2D)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); + void (*glGenBuffers)(GLsizei n, GLuint *buffers); + void (*glGenFramebuffers)(GLsizei n, GLuint *framebuffers); + void (*glGenRenderbuffers)(GLsizei n, GLuint *renderbuffers); + void (*glGenTextures)(GLsizei n, GLuint *textures); + GLenum (*glGetError)(void); + void (*glGetFramebufferAttachmentParameteriv)(GLenum target, GLenum attachment, GLenum pname, GLint *params); + void (*glGetFloatv)(GLenum pname, GLfloat *data); + void (*glGetIntegerv)(GLenum pname, GLint *data); + void (*glGetIntegeri_v)(GLenum pname, GLuint idx, GLint *data); + void (*glGetProgramiv)(GLuint program, GLenum pname, GLint *params); + void (*glGetProgramInfoLog)(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); + void (*glGetRenderbufferParameteriv)(GLenum target, GLenum pname, GLint *params); + void (*glGetShaderiv)(GLuint shader, GLenum pname, GLint *params); + void (*glGetShaderInfoLog)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); + const GLubyte *(*glGetString)(GLenum name); + GLint (*glGetUniformLocation)(GLuint program, const GLchar *name); + void (*glGetVertexAttribiv)(GLuint index, GLenum pname, GLint *params); + void (*glGetVertexAttribPointerv)(GLuint index, GLenum pname, void **params); + GLboolean (*glIsEnabled)(GLenum cap); + void (*glLinkProgram)(GLuint program); + void (*glPixelStorei)(GLenum pname, GLint param); + void (*glReadPixels)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels); + void (*glRenderbufferStorage)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); + void (*glScissor)(GLint x, GLint y, GLsizei width, GLsizei height); + void (*glShaderSource)(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); + void (*glTexImage2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); + void (*glTexParameteri)(GLenum target, GLenum pname, GLint param); + void (*glTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); + void (*glUniform1f)(GLint location, GLfloat v0); + void (*glUniform1i)(GLint location, GLint v0); + void (*glUniform2f)(GLint location, GLfloat v0, GLfloat v1); + void (*glUniform3f)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); + void (*glUniform4f)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); + void (*glUseProgram)(GLuint program); + void (*glVertexAttribPointer)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); + void (*glViewport)(GLint x, GLint y, GLsizei width, GLsizei height); + + void (*glBindVertexArray)(GLuint array); + void (*glBindBufferBase)(GLenum target, GLuint index, GLuint buffer); + GLuint (*glGetUniformBlockIndex)(GLuint program, const GLchar *uniformBlockName); + void (*glUniformBlockBinding)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); + void (*glInvalidateFramebuffer)(GLenum target, GLsizei numAttachments, const GLenum *attachments); + void (*glBeginQuery)(GLenum target, GLuint id); + void (*glDeleteQueries)(GLsizei n, const GLuint *ids); + void (*glDeleteVertexArrays)(GLsizei n, const GLuint *ids); + void (*glEndQuery)(GLenum target); + void (*glGenQueries)(GLsizei n, GLuint *ids); + void (*glGenVertexArrays)(GLsizei n, GLuint *ids); + void (*glGetProgramBinary)(GLuint program, GLsizei bufsize, GLsizei *length, GLenum *binaryFormat, void *binary); + void (*glGetQueryObjectuiv)(GLuint id, GLenum pname, GLuint *params); + const GLubyte* (*glGetStringi)(GLenum name, GLuint index); + void (*glDispatchCompute)(GLuint x, GLuint y, GLuint z); + void (*glMemoryBarrier)(GLbitfield barriers); + void* (*glMapBufferRange)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); + GLboolean (*glUnmapBuffer)(GLenum target); + void (*glBindImageTexture)(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); + void (*glTexStorage2D)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); + void (*glBlitFramebuffer)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +} glFunctions; + +static void glActiveTexture(glFunctions *f, GLenum texture) { + f->glActiveTexture(texture); +} + +static void glAttachShader(glFunctions *f, GLuint program, GLuint shader) { + f->glAttachShader(program, shader); +} + +static void glBindAttribLocation(glFunctions *f, GLuint program, GLuint index, const GLchar *name) { + f->glBindAttribLocation(program, index, name); +} + +static void glBindBuffer(glFunctions *f, GLenum target, GLuint buffer) { + f->glBindBuffer(target, buffer); +} + +static void glBindFramebuffer(glFunctions *f, GLenum target, GLuint framebuffer) { + f->glBindFramebuffer(target, framebuffer); +} + +static void glBindRenderbuffer(glFunctions *f, GLenum target, GLuint renderbuffer) { + f->glBindRenderbuffer(target, renderbuffer); +} + +static void glBindTexture(glFunctions *f, GLenum target, GLuint texture) { + f->glBindTexture(target, texture); +} + +static void glBindVertexArray(glFunctions *f, GLuint array) { + f->glBindVertexArray(array); +} + +static void glBlendEquation(glFunctions *f, GLenum mode) { + f->glBlendEquation(mode); +} + +static void glBlendFuncSeparate(glFunctions *f, GLenum srcRGB, GLenum dstRGB, GLenum srcA, GLenum dstA) { + f->glBlendFuncSeparate(srcRGB, dstRGB, srcA, dstA); +} + +static void glBufferData(glFunctions *f, GLenum target, GLsizeiptr size, const void *data, GLenum usage) { + f->glBufferData(target, size, data, usage); +} + +static void glBufferSubData(glFunctions *f, GLenum target, GLintptr offset, GLsizeiptr size, const void *data) { + f->glBufferSubData(target, offset, size, data); +} + +static GLenum glCheckFramebufferStatus(glFunctions *f, GLenum target) { + return f->glCheckFramebufferStatus(target); +} + +static void glClear(glFunctions *f, GLbitfield mask) { + f->glClear(mask); +} + +static void glClearColor(glFunctions *f, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) { + f->glClearColor(red, green, blue, alpha); +} + +static void glClearDepthf(glFunctions *f, GLfloat d) { + f->glClearDepthf(d); +} + +static void glCompileShader(glFunctions *f, GLuint shader) { + f->glCompileShader(shader); +} + +static void glCopyTexSubImage2D(glFunctions *f, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) { + f->glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); +} + +static GLuint glCreateProgram(glFunctions *f) { + return f->glCreateProgram(); +} + +static GLuint glCreateShader(glFunctions *f, GLenum type) { + return f->glCreateShader(type); +} + +static void glDeleteBuffers(glFunctions *f, GLsizei n, const GLuint *buffers) { + f->glDeleteBuffers(n, buffers); +} + +static void glDeleteFramebuffers(glFunctions *f, GLsizei n, const GLuint *framebuffers) { + f->glDeleteFramebuffers(n, framebuffers); +} + +static void glDeleteProgram(glFunctions *f, GLuint program) { + f->glDeleteProgram(program); +} + +static void glDeleteRenderbuffers(glFunctions *f, GLsizei n, const GLuint *renderbuffers) { + f->glDeleteRenderbuffers(n, renderbuffers); +} + +static void glDeleteShader(glFunctions *f, GLuint shader) { + f->glDeleteShader(shader); +} + +static void glDeleteTextures(glFunctions *f, GLsizei n, const GLuint *textures) { + f->glDeleteTextures(n, textures); +} + +static void glDepthFunc(glFunctions *f, GLenum func) { + f->glDepthFunc(func); +} + +static void glDepthMask(glFunctions *f, GLboolean flag) { + f->glDepthMask(flag); +} + +static void glDisable(glFunctions *f, GLenum cap) { + f->glDisable(cap); +} + +static void glDisableVertexAttribArray(glFunctions *f, GLuint index) { + f->glDisableVertexAttribArray(index); +} + +static void glDrawArrays(glFunctions *f, GLenum mode, GLint first, GLsizei count) { + f->glDrawArrays(mode, first, count); +} + +// offset is defined as an uintptr_t to omit Cgo pointer checks. +static void glDrawElements(glFunctions *f, GLenum mode, GLsizei count, GLenum type, const uintptr_t offset) { + f->glDrawElements(mode, count, type, (const void *)offset); +} + +static void glEnable(glFunctions *f, GLenum cap) { + f->glEnable(cap); +} + +static void glEnableVertexAttribArray(glFunctions *f, GLuint index) { + f->glEnableVertexAttribArray(index); +} + +static void glFinish(glFunctions *f) { + f->glFinish(); +} + +static void glFlush(glFunctions *f) { + f->glFlush(); +} + +static void glFramebufferRenderbuffer(glFunctions *f, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) { + f->glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer); +} + +static void glFramebufferTexture2D(glFunctions *f, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) { + f->glFramebufferTexture2D(target, attachment, textarget, texture, level); +} + +static void glGenBuffers(glFunctions *f, GLsizei n, GLuint *buffers) { + f->glGenBuffers(n, buffers); +} + +static void glGenFramebuffers(glFunctions *f, GLsizei n, GLuint *framebuffers) { + f->glGenFramebuffers(n, framebuffers); +} + +static void glGenRenderbuffers(glFunctions *f, GLsizei n, GLuint *renderbuffers) { + f->glGenRenderbuffers(n, renderbuffers); +} + +static void glGenTextures(glFunctions *f, GLsizei n, GLuint *textures) { + f->glGenTextures(n, textures); +} + +static GLenum glGetError(glFunctions *f) { + return f->glGetError(); +} + +static void glGetFramebufferAttachmentParameteriv(glFunctions *f, GLenum target, GLenum attachment, GLenum pname, GLint *params) { + f->glGetFramebufferAttachmentParameteriv(target, attachment, pname, params); +} + +static void glGetIntegerv(glFunctions *f, GLenum pname, GLint *data) { + f->glGetIntegerv(pname, data); +} + +static void glGetFloatv(glFunctions *f, GLenum pname, GLfloat *data) { + f->glGetFloatv(pname, data); +} + +static void glGetIntegeri_v(glFunctions *f, GLenum pname, GLuint idx, GLint *data) { + f->glGetIntegeri_v(pname, idx, data); +} + +static void glGetProgramiv(glFunctions *f, GLuint program, GLenum pname, GLint *params) { + f->glGetProgramiv(program, pname, params); +} + +static void glGetProgramInfoLog(glFunctions *f, GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog) { + f->glGetProgramInfoLog(program, bufSize, length, infoLog); +} + +static void glGetRenderbufferParameteriv(glFunctions *f, GLenum target, GLenum pname, GLint *params) { + f->glGetRenderbufferParameteriv(target, pname, params); +} + +static void glGetShaderiv(glFunctions *f, GLuint shader, GLenum pname, GLint *params) { + f->glGetShaderiv(shader, pname, params); +} + +static void glGetShaderInfoLog(glFunctions *f, GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog) { + f->glGetShaderInfoLog(shader, bufSize, length, infoLog); +} + +static const GLubyte *glGetString(glFunctions *f, GLenum name) { + return f->glGetString(name); +} + +static GLint glGetUniformLocation(glFunctions *f, GLuint program, const GLchar *name) { + return f->glGetUniformLocation(program, name); +} + +static void glGetVertexAttribiv(glFunctions *f, GLuint index, GLenum pname, GLint *data) { + f->glGetVertexAttribiv(index, pname, data); +} + +// Return uintptr_t to avoid Cgo pointer check. +static uintptr_t glGetVertexAttribPointerv(glFunctions *f, GLuint index, GLenum pname) { + void *ptrs; + f->glGetVertexAttribPointerv(index, pname, &ptrs); + return (uintptr_t)ptrs; +} + +static GLboolean glIsEnabled(glFunctions *f, GLenum cap) { + return f->glIsEnabled(cap); +} + +static void glLinkProgram(glFunctions *f, GLuint program) { + f->glLinkProgram(program); +} + +static void glPixelStorei(glFunctions *f, GLenum pname, GLint param) { + f->glPixelStorei(pname, param); +} + +static void glReadPixels(glFunctions *f, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels) { + f->glReadPixels(x, y, width, height, format, type, pixels); +} + +static void glRenderbufferStorage(glFunctions *f, GLenum target, GLenum internalformat, GLsizei width, GLsizei height) { + f->glRenderbufferStorage(target, internalformat, width, height); +} + +static void glScissor(glFunctions *f, GLint x, GLint y, GLsizei width, GLsizei height) { + f->glScissor(x, y, width, height); +} + +static void glShaderSource(glFunctions *f, GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length) { + f->glShaderSource(shader, count, string, length); +} + +static void glTexImage2D(glFunctions *f, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) { + f->glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels); +} + +static void glTexParameteri(glFunctions *f, GLenum target, GLenum pname, GLint param) { + f->glTexParameteri(target, pname, param); +} + +static void glTexSubImage2D(glFunctions *f, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) { + f->glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); +} + +static void glUniform1f(glFunctions *f, GLint location, GLfloat v0) { + f->glUniform1f(location, v0); +} + +static void glUniform1i(glFunctions *f, GLint location, GLint v0) { + f->glUniform1i(location, v0); +} + +static void glUniform2f(glFunctions *f, GLint location, GLfloat v0, GLfloat v1) { + f->glUniform2f(location, v0, v1); +} + +static void glUniform3f(glFunctions *f, GLint location, GLfloat v0, GLfloat v1, GLfloat v2) { + f->glUniform3f(location, v0, v1, v2); +} + +static void glUniform4f(glFunctions *f, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) { + f->glUniform4f(location, v0, v1, v2, v3); +} + +static void glUseProgram(glFunctions *f, GLuint program) { + f->glUseProgram(program); +} + +// offset is defined as an uintptr_t to omit Cgo pointer checks. +static void glVertexAttribPointer(glFunctions *f, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, uintptr_t offset) { + f->glVertexAttribPointer(index, size, type, normalized, stride, (const void *)offset); +} + +static void glViewport(glFunctions *f, GLint x, GLint y, GLsizei width, GLsizei height) { + f->glViewport(x, y, width, height); +} + +static void glBindBufferBase(glFunctions *f, GLenum target, GLuint index, GLuint buffer) { + f->glBindBufferBase(target, index, buffer); +} + +static void glUniformBlockBinding(glFunctions *f, GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding) { + f->glUniformBlockBinding(program, uniformBlockIndex, uniformBlockBinding); +} + +static GLuint glGetUniformBlockIndex(glFunctions *f, GLuint program, const GLchar *uniformBlockName) { + return f->glGetUniformBlockIndex(program, uniformBlockName); +} + +static void glInvalidateFramebuffer(glFunctions *f, GLenum target, GLenum attachment) { + // Framebuffer invalidation is just a hint and can safely be ignored. + if (f->glInvalidateFramebuffer != NULL) { + f->glInvalidateFramebuffer(target, 1, &attachment); + } +} + +static void glBeginQuery(glFunctions *f, GLenum target, GLenum attachment) { + f->glBeginQuery(target, attachment); +} + +static void glDeleteQueries(glFunctions *f, GLsizei n, const GLuint *ids) { + f->glDeleteQueries(n, ids); +} + +static void glDeleteVertexArrays(glFunctions *f, GLsizei n, const GLuint *ids) { + f->glDeleteVertexArrays(n, ids); +} + +static void glEndQuery(glFunctions *f, GLenum target) { + f->glEndQuery(target); +} + +static const GLubyte* glGetStringi(glFunctions *f, GLenum name, GLuint index) { + return f->glGetStringi(name, index); +} + +static void glGenQueries(glFunctions *f, GLsizei n, GLuint *ids) { + f->glGenQueries(n, ids); +} + +static void glGenVertexArrays(glFunctions *f, GLsizei n, GLuint *ids) { + f->glGenVertexArrays(n, ids); +} + +static void glGetProgramBinary(glFunctions *f, GLuint program, GLsizei bufsize, GLsizei *length, GLenum *binaryFormat, void *binary) { + f->glGetProgramBinary(program, bufsize, length, binaryFormat, binary); +} + +static void glGetQueryObjectuiv(glFunctions *f, GLuint id, GLenum pname, GLuint *params) { + f->glGetQueryObjectuiv(id, pname, params); +} + +static void glMemoryBarrier(glFunctions *f, GLbitfield barriers) { + f->glMemoryBarrier(barriers); +} + +static void glDispatchCompute(glFunctions *f, GLuint x, GLuint y, GLuint z) { + f->glDispatchCompute(x, y, z); +} + +static void *glMapBufferRange(glFunctions *f, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) { + return f->glMapBufferRange(target, offset, length, access); +} + +static GLboolean glUnmapBuffer(glFunctions *f, GLenum target) { + return f->glUnmapBuffer(target); +} + +static void glBindImageTexture(glFunctions *f, GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) { + f->glBindImageTexture(unit, texture, level, layered, layer, access, format); +} + +static void glTexStorage2D(glFunctions *f, GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height) { + f->glTexStorage2D(target, levels, internalFormat, width, height); +} + +static void glBlitFramebuffer(glFunctions *f, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) { + f->glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); +} +*/ +import "C" + +type Context interface{} + +type Functions struct { + // Query caches. + uints [100]C.GLuint + ints [100]C.GLint + floats [100]C.GLfloat + + f C.glFunctions +} + +func NewFunctions(ctx Context, forceES bool) (*Functions, error) { + if ctx != nil { + panic("non-nil context") + } + f := new(Functions) + err := f.load(forceES) + if err != nil { + return nil, err + } + return f, nil +} + +func dlsym(handle unsafe.Pointer, s string) unsafe.Pointer { + cs := C.CString(s) + defer C.free(unsafe.Pointer(cs)) + return C.dlsym(handle, cs) +} + +func dlopen(lib string) unsafe.Pointer { + clib := C.CString(lib) + defer C.free(unsafe.Pointer(clib)) + return C.dlopen(clib, C.RTLD_NOW|C.RTLD_LOCAL) +} + +func (f *Functions) load(forceES bool) error { + var ( + loadErr error + libNames []string + handles []unsafe.Pointer + ) + switch { + case runtime.GOOS == "darwin" && !forceES: + libNames = []string{"/System/Library/Frameworks/OpenGL.framework/OpenGL"} + case runtime.GOOS == "darwin" && forceES: + libNames = []string{"libGLESv2.dylib"} + case runtime.GOOS == "ios": + libNames = []string{"/System/Library/Frameworks/OpenGLES.framework/OpenGLES"} + case runtime.GOOS == "android": + libNames = []string{"libGLESv2.so", "libGLESv3.so"} + default: + libNames = []string{"libGLESv2.so.2"} + } + for _, lib := range libNames { + if h := dlopen(lib); h != nil { + handles = append(handles, h) + } + } + if len(handles) == 0 { + return fmt.Errorf("gl: no OpenGL implementation could be loaded (tried %q)", libNames) + } + load := func(s string) *[0]byte { + for _, h := range handles { + if f := dlsym(h, s); f != nil { + return (*[0]byte)(f) + } + } + return nil + } + must := func(s string) *[0]byte { + ptr := load(s) + if ptr == nil { + loadErr = fmt.Errorf("gl: failed to load symbol %q", s) + } + return ptr + } + // GL ES 2.0 functions. + f.f.glActiveTexture = must("glActiveTexture") + f.f.glAttachShader = must("glAttachShader") + f.f.glBindAttribLocation = must("glBindAttribLocation") + f.f.glBindBuffer = must("glBindBuffer") + f.f.glBindFramebuffer = must("glBindFramebuffer") + f.f.glBindRenderbuffer = must("glBindRenderbuffer") + f.f.glBindTexture = must("glBindTexture") + f.f.glBlendEquation = must("glBlendEquation") + f.f.glBlendFuncSeparate = must("glBlendFuncSeparate") + f.f.glBufferData = must("glBufferData") + f.f.glBufferSubData = must("glBufferSubData") + f.f.glCheckFramebufferStatus = must("glCheckFramebufferStatus") + f.f.glClear = must("glClear") + f.f.glClearColor = must("glClearColor") + f.f.glClearDepthf = must("glClearDepthf") + f.f.glCompileShader = must("glCompileShader") + f.f.glCopyTexSubImage2D = must("glCopyTexSubImage2D") + f.f.glCreateProgram = must("glCreateProgram") + f.f.glCreateShader = must("glCreateShader") + f.f.glDeleteBuffers = must("glDeleteBuffers") + f.f.glDeleteFramebuffers = must("glDeleteFramebuffers") + f.f.glDeleteProgram = must("glDeleteProgram") + f.f.glDeleteRenderbuffers = must("glDeleteRenderbuffers") + f.f.glDeleteShader = must("glDeleteShader") + f.f.glDeleteTextures = must("glDeleteTextures") + f.f.glDepthFunc = must("glDepthFunc") + f.f.glDepthMask = must("glDepthMask") + f.f.glDisable = must("glDisable") + f.f.glDisableVertexAttribArray = must("glDisableVertexAttribArray") + f.f.glDrawArrays = must("glDrawArrays") + f.f.glDrawElements = must("glDrawElements") + f.f.glEnable = must("glEnable") + f.f.glEnableVertexAttribArray = must("glEnableVertexAttribArray") + f.f.glFinish = must("glFinish") + f.f.glFlush = must("glFlush") + f.f.glFramebufferRenderbuffer = must("glFramebufferRenderbuffer") + f.f.glFramebufferTexture2D = must("glFramebufferTexture2D") + f.f.glGenBuffers = must("glGenBuffers") + f.f.glGenFramebuffers = must("glGenFramebuffers") + f.f.glGenRenderbuffers = must("glGenRenderbuffers") + f.f.glGenTextures = must("glGenTextures") + f.f.glGetError = must("glGetError") + f.f.glGetFramebufferAttachmentParameteriv = must("glGetFramebufferAttachmentParameteriv") + f.f.glGetIntegerv = must("glGetIntegerv") + f.f.glGetFloatv = must("glGetFloatv") + f.f.glGetProgramiv = must("glGetProgramiv") + f.f.glGetProgramInfoLog = must("glGetProgramInfoLog") + f.f.glGetRenderbufferParameteriv = must("glGetRenderbufferParameteriv") + f.f.glGetShaderiv = must("glGetShaderiv") + f.f.glGetShaderInfoLog = must("glGetShaderInfoLog") + f.f.glGetString = must("glGetString") + f.f.glGetUniformLocation = must("glGetUniformLocation") + f.f.glGetVertexAttribiv = must("glGetVertexAttribiv") + f.f.glGetVertexAttribPointerv = must("glGetVertexAttribPointerv") + f.f.glIsEnabled = must("glIsEnabled") + f.f.glLinkProgram = must("glLinkProgram") + f.f.glPixelStorei = must("glPixelStorei") + f.f.glReadPixels = must("glReadPixels") + f.f.glRenderbufferStorage = must("glRenderbufferStorage") + f.f.glScissor = must("glScissor") + f.f.glShaderSource = must("glShaderSource") + f.f.glTexImage2D = must("glTexImage2D") + f.f.glTexParameteri = must("glTexParameteri") + f.f.glTexSubImage2D = must("glTexSubImage2D") + f.f.glUniform1f = must("glUniform1f") + f.f.glUniform1i = must("glUniform1i") + f.f.glUniform2f = must("glUniform2f") + f.f.glUniform3f = must("glUniform3f") + f.f.glUniform4f = must("glUniform4f") + f.f.glUseProgram = must("glUseProgram") + f.f.glVertexAttribPointer = must("glVertexAttribPointer") + f.f.glViewport = must("glViewport") + + // Extensions and GL ES 3 functions. + f.f.glBindBufferBase = load("glBindBufferBase") + f.f.glBindVertexArray = load("glBindVertexArray") + f.f.glGetIntegeri_v = load("glGetIntegeri_v") + f.f.glGetUniformBlockIndex = load("glGetUniformBlockIndex") + f.f.glUniformBlockBinding = load("glUniformBlockBinding") + f.f.glInvalidateFramebuffer = load("glInvalidateFramebuffer") + f.f.glGetStringi = load("glGetStringi") + // Fall back to EXT_invalidate_framebuffer if available. + if f.f.glInvalidateFramebuffer == nil { + f.f.glInvalidateFramebuffer = load("glDiscardFramebufferEXT") + } + + f.f.glBeginQuery = load("glBeginQuery") + if f.f.glBeginQuery == nil { + f.f.glBeginQuery = load("glBeginQueryEXT") + } + f.f.glDeleteQueries = load("glDeleteQueries") + if f.f.glDeleteQueries == nil { + f.f.glDeleteQueries = load("glDeleteQueriesEXT") + } + f.f.glEndQuery = load("glEndQuery") + if f.f.glEndQuery == nil { + f.f.glEndQuery = load("glEndQueryEXT") + } + f.f.glGenQueries = load("glGenQueries") + if f.f.glGenQueries == nil { + f.f.glGenQueries = load("glGenQueriesEXT") + } + f.f.glGetQueryObjectuiv = load("glGetQueryObjectuiv") + if f.f.glGetQueryObjectuiv == nil { + f.f.glGetQueryObjectuiv = load("glGetQueryObjectuivEXT") + } + + f.f.glDeleteVertexArrays = load("glDeleteVertexArrays") + f.f.glGenVertexArrays = load("glGenVertexArrays") + f.f.glMemoryBarrier = load("glMemoryBarrier") + f.f.glDispatchCompute = load("glDispatchCompute") + f.f.glMapBufferRange = load("glMapBufferRange") + f.f.glUnmapBuffer = load("glUnmapBuffer") + f.f.glBindImageTexture = load("glBindImageTexture") + f.f.glTexStorage2D = load("glTexStorage2D") + f.f.glBlitFramebuffer = load("glBlitFramebuffer") + f.f.glGetProgramBinary = load("glGetProgramBinary") + + return loadErr +} + +func (f *Functions) ActiveTexture(texture Enum) { + C.glActiveTexture(&f.f, C.GLenum(texture)) +} + +func (f *Functions) AttachShader(p Program, s Shader) { + C.glAttachShader(&f.f, C.GLuint(p.V), C.GLuint(s.V)) +} + +func (f *Functions) BeginQuery(target Enum, query Query) { + C.glBeginQuery(&f.f, C.GLenum(target), C.GLenum(query.V)) +} + +func (f *Functions) BindAttribLocation(p Program, a Attrib, name string) { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + C.glBindAttribLocation(&f.f, C.GLuint(p.V), C.GLuint(a), cname) +} + +func (f *Functions) BindBufferBase(target Enum, index int, b Buffer) { + C.glBindBufferBase(&f.f, C.GLenum(target), C.GLuint(index), C.GLuint(b.V)) +} + +func (f *Functions) BindBuffer(target Enum, b Buffer) { + C.glBindBuffer(&f.f, C.GLenum(target), C.GLuint(b.V)) +} + +func (f *Functions) BindFramebuffer(target Enum, fb Framebuffer) { + C.glBindFramebuffer(&f.f, C.GLenum(target), C.GLuint(fb.V)) +} + +func (f *Functions) BindRenderbuffer(target Enum, fb Renderbuffer) { + C.glBindRenderbuffer(&f.f, C.GLenum(target), C.GLuint(fb.V)) +} + +func (f *Functions) BindImageTexture(unit int, t Texture, level int, layered bool, layer int, access, format Enum) { + l := C.GLboolean(FALSE) + if layered { + l = TRUE + } + C.glBindImageTexture(&f.f, C.GLuint(unit), C.GLuint(t.V), C.GLint(level), l, C.GLint(layer), C.GLenum(access), C.GLenum(format)) +} + +func (f *Functions) BindTexture(target Enum, t Texture) { + C.glBindTexture(&f.f, C.GLenum(target), C.GLuint(t.V)) +} + +func (f *Functions) BindVertexArray(a VertexArray) { + C.glBindVertexArray(&f.f, C.GLuint(a.V)) +} + +func (f *Functions) BlendEquation(mode Enum) { + C.glBlendEquation(&f.f, C.GLenum(mode)) +} + +func (f *Functions) BlendFuncSeparate(srcRGB, dstRGB, srcA, dstA Enum) { + C.glBlendFuncSeparate(&f.f, C.GLenum(srcRGB), C.GLenum(dstRGB), C.GLenum(srcA), C.GLenum(dstA)) +} + +func (f *Functions) BlitFramebuffer(sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1 int, mask Enum, filter Enum) { + C.glBlitFramebuffer(&f.f, + C.GLint(sx0), C.GLint(sy0), C.GLint(sx1), C.GLint(sy1), + C.GLint(dx0), C.GLint(dy0), C.GLint(dx1), C.GLint(dy1), + C.GLenum(mask), C.GLenum(filter), + ) +} + +func (f *Functions) BufferData(target Enum, size int, usage Enum, data []byte) { + var p unsafe.Pointer + if len(data) > 0 { + p = unsafe.Pointer(&data[0]) + } + C.glBufferData(&f.f, C.GLenum(target), C.GLsizeiptr(size), p, C.GLenum(usage)) +} + +func (f *Functions) BufferSubData(target Enum, offset int, src []byte) { + var p unsafe.Pointer + if len(src) > 0 { + p = unsafe.Pointer(&src[0]) + } + C.glBufferSubData(&f.f, C.GLenum(target), C.GLintptr(offset), C.GLsizeiptr(len(src)), p) +} + +func (f *Functions) CheckFramebufferStatus(target Enum) Enum { + return Enum(C.glCheckFramebufferStatus(&f.f, C.GLenum(target))) +} + +func (f *Functions) Clear(mask Enum) { + C.glClear(&f.f, C.GLbitfield(mask)) +} + +func (f *Functions) ClearColor(red float32, green float32, blue float32, alpha float32) { + C.glClearColor(&f.f, C.GLfloat(red), C.GLfloat(green), C.GLfloat(blue), C.GLfloat(alpha)) +} + +func (f *Functions) ClearDepthf(d float32) { + C.glClearDepthf(&f.f, C.GLfloat(d)) +} + +func (f *Functions) CompileShader(s Shader) { + C.glCompileShader(&f.f, C.GLuint(s.V)) +} + +func (f *Functions) CopyTexSubImage2D(target Enum, level, xoffset, yoffset, x, y, width, height int) { + C.glCopyTexSubImage2D(&f.f, C.GLenum(target), C.GLint(level), C.GLint(xoffset), C.GLint(yoffset), C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height)) +} + +func (f *Functions) CreateBuffer() Buffer { + C.glGenBuffers(&f.f, 1, &f.uints[0]) + return Buffer{uint(f.uints[0])} +} + +func (f *Functions) CreateFramebuffer() Framebuffer { + C.glGenFramebuffers(&f.f, 1, &f.uints[0]) + return Framebuffer{uint(f.uints[0])} +} + +func (f *Functions) CreateProgram() Program { + return Program{uint(C.glCreateProgram(&f.f))} +} + +func (f *Functions) CreateQuery() Query { + C.glGenQueries(&f.f, 1, &f.uints[0]) + return Query{uint(f.uints[0])} +} + +func (f *Functions) CreateRenderbuffer() Renderbuffer { + C.glGenRenderbuffers(&f.f, 1, &f.uints[0]) + return Renderbuffer{uint(f.uints[0])} +} + +func (f *Functions) CreateShader(ty Enum) Shader { + return Shader{uint(C.glCreateShader(&f.f, C.GLenum(ty)))} +} + +func (f *Functions) CreateTexture() Texture { + C.glGenTextures(&f.f, 1, &f.uints[0]) + return Texture{uint(f.uints[0])} +} + +func (f *Functions) CreateVertexArray() VertexArray { + C.glGenVertexArrays(&f.f, 1, &f.uints[0]) + return VertexArray{uint(f.uints[0])} +} + +func (f *Functions) DeleteBuffer(v Buffer) { + f.uints[0] = C.GLuint(v.V) + C.glDeleteBuffers(&f.f, 1, &f.uints[0]) +} + +func (f *Functions) DeleteFramebuffer(v Framebuffer) { + f.uints[0] = C.GLuint(v.V) + C.glDeleteFramebuffers(&f.f, 1, &f.uints[0]) +} + +func (f *Functions) DeleteProgram(p Program) { + C.glDeleteProgram(&f.f, C.GLuint(p.V)) +} + +func (f *Functions) DeleteQuery(query Query) { + f.uints[0] = C.GLuint(query.V) + C.glDeleteQueries(&f.f, 1, &f.uints[0]) +} + +func (f *Functions) DeleteVertexArray(array VertexArray) { + f.uints[0] = C.GLuint(array.V) + C.glDeleteVertexArrays(&f.f, 1, &f.uints[0]) +} + +func (f *Functions) DeleteRenderbuffer(v Renderbuffer) { + f.uints[0] = C.GLuint(v.V) + C.glDeleteRenderbuffers(&f.f, 1, &f.uints[0]) +} + +func (f *Functions) DeleteShader(s Shader) { + C.glDeleteShader(&f.f, C.GLuint(s.V)) +} + +func (f *Functions) DeleteTexture(v Texture) { + f.uints[0] = C.GLuint(v.V) + C.glDeleteTextures(&f.f, 1, &f.uints[0]) +} + +func (f *Functions) DepthFunc(v Enum) { + C.glDepthFunc(&f.f, C.GLenum(v)) +} + +func (f *Functions) DepthMask(mask bool) { + m := C.GLboolean(FALSE) + if mask { + m = C.GLboolean(TRUE) + } + C.glDepthMask(&f.f, m) +} + +func (f *Functions) DisableVertexAttribArray(a Attrib) { + C.glDisableVertexAttribArray(&f.f, C.GLuint(a)) +} + +func (f *Functions) Disable(cap Enum) { + C.glDisable(&f.f, C.GLenum(cap)) +} + +func (f *Functions) DrawArrays(mode Enum, first int, count int) { + C.glDrawArrays(&f.f, C.GLenum(mode), C.GLint(first), C.GLsizei(count)) +} + +func (f *Functions) DrawElements(mode Enum, count int, ty Enum, offset int) { + C.glDrawElements(&f.f, C.GLenum(mode), C.GLsizei(count), C.GLenum(ty), C.uintptr_t(offset)) +} + +func (f *Functions) DispatchCompute(x, y, z int) { + C.glDispatchCompute(&f.f, C.GLuint(x), C.GLuint(y), C.GLuint(z)) +} + +func (f *Functions) Enable(cap Enum) { + C.glEnable(&f.f, C.GLenum(cap)) +} + +func (f *Functions) EndQuery(target Enum) { + C.glEndQuery(&f.f, C.GLenum(target)) +} + +func (f *Functions) EnableVertexAttribArray(a Attrib) { + C.glEnableVertexAttribArray(&f.f, C.GLuint(a)) +} + +func (f *Functions) Finish() { + C.glFinish(&f.f) +} + +func (f *Functions) Flush() { + C.glFlush(&f.f) +} + +func (f *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget Enum, renderbuffer Renderbuffer) { + C.glFramebufferRenderbuffer(&f.f, C.GLenum(target), C.GLenum(attachment), C.GLenum(renderbuffertarget), C.GLuint(renderbuffer.V)) +} + +func (f *Functions) FramebufferTexture2D(target, attachment, texTarget Enum, t Texture, level int) { + C.glFramebufferTexture2D(&f.f, C.GLenum(target), C.GLenum(attachment), C.GLenum(texTarget), C.GLuint(t.V), C.GLint(level)) +} + +func (c *Functions) GetBinding(pname Enum) Object { + return Object{uint(c.GetInteger(pname))} +} + +func (c *Functions) GetBindingi(pname Enum, idx int) Object { + return Object{uint(c.GetIntegeri(pname, idx))} +} + +func (f *Functions) GetError() Enum { + return Enum(C.glGetError(&f.f)) +} + +func (f *Functions) GetRenderbufferParameteri(target, pname Enum) int { + C.glGetRenderbufferParameteriv(&f.f, C.GLenum(target), C.GLenum(pname), &f.ints[0]) + return int(f.ints[0]) +} + +func (f *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname Enum) int { + C.glGetFramebufferAttachmentParameteriv(&f.f, C.GLenum(target), C.GLenum(attachment), C.GLenum(pname), &f.ints[0]) + return int(f.ints[0]) +} + +func (f *Functions) GetFloat4(pname Enum) [4]float32 { + C.glGetFloatv(&f.f, C.GLenum(pname), &f.floats[0]) + var r [4]float32 + for i := range r { + r[i] = float32(f.floats[i]) + } + return r +} + +func (f *Functions) GetFloat(pname Enum) float32 { + C.glGetFloatv(&f.f, C.GLenum(pname), &f.floats[0]) + return float32(f.floats[0]) +} + +func (f *Functions) GetInteger4(pname Enum) [4]int { + C.glGetIntegerv(&f.f, C.GLenum(pname), &f.ints[0]) + var r [4]int + for i := range r { + r[i] = int(f.ints[i]) + } + return r +} + +func (f *Functions) GetInteger(pname Enum) int { + C.glGetIntegerv(&f.f, C.GLenum(pname), &f.ints[0]) + return int(f.ints[0]) +} + +func (f *Functions) GetIntegeri(pname Enum, idx int) int { + C.glGetIntegeri_v(&f.f, C.GLenum(pname), C.GLuint(idx), &f.ints[0]) + return int(f.ints[0]) +} + +func (f *Functions) GetProgrami(p Program, pname Enum) int { + C.glGetProgramiv(&f.f, C.GLuint(p.V), C.GLenum(pname), &f.ints[0]) + return int(f.ints[0]) +} + +func (f *Functions) GetProgramBinary(p Program) []byte { + sz := f.GetProgrami(p, PROGRAM_BINARY_LENGTH) + if sz == 0 { + return nil + } + buf := make([]byte, sz) + var format C.GLenum + C.glGetProgramBinary(&f.f, C.GLuint(p.V), C.GLsizei(sz), nil, &format, unsafe.Pointer(&buf[0])) + return buf +} + +func (f *Functions) GetProgramInfoLog(p Program) string { + n := f.GetProgrami(p, INFO_LOG_LENGTH) + buf := make([]byte, n) + C.glGetProgramInfoLog(&f.f, C.GLuint(p.V), C.GLsizei(len(buf)), nil, (*C.GLchar)(unsafe.Pointer(&buf[0]))) + return string(buf) +} + +func (f *Functions) GetQueryObjectuiv(query Query, pname Enum) uint { + C.glGetQueryObjectuiv(&f.f, C.GLuint(query.V), C.GLenum(pname), &f.uints[0]) + return uint(f.uints[0]) +} + +func (f *Functions) GetShaderi(s Shader, pname Enum) int { + C.glGetShaderiv(&f.f, C.GLuint(s.V), C.GLenum(pname), &f.ints[0]) + return int(f.ints[0]) +} + +func (f *Functions) GetShaderInfoLog(s Shader) string { + n := f.GetShaderi(s, INFO_LOG_LENGTH) + buf := make([]byte, n) + C.glGetShaderInfoLog(&f.f, C.GLuint(s.V), C.GLsizei(len(buf)), nil, (*C.GLchar)(unsafe.Pointer(&buf[0]))) + return string(buf) +} + +func (f *Functions) getStringi(pname Enum, index int) string { + str := C.glGetStringi(&f.f, C.GLenum(pname), C.GLuint(index)) + if str == nil { + return "" + } + return C.GoString((*C.char)(unsafe.Pointer(str))) +} + +func (f *Functions) GetString(pname Enum) string { + switch { + case runtime.GOOS == "darwin" && pname == EXTENSIONS: + // macOS OpenGL 3 core profile doesn't support glGetString(GL_EXTENSIONS). + // Use glGetStringi(GL_EXTENSIONS, <index>). + var exts []string + nexts := f.GetInteger(NUM_EXTENSIONS) + for i := 0; i < nexts; i++ { + ext := f.getStringi(EXTENSIONS, i) + exts = append(exts, ext) + } + return strings.Join(exts, " ") + default: + str := C.glGetString(&f.f, C.GLenum(pname)) + return C.GoString((*C.char)(unsafe.Pointer(str))) + } +} + +func (f *Functions) GetUniformBlockIndex(p Program, name string) uint { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + return uint(C.glGetUniformBlockIndex(&f.f, C.GLuint(p.V), cname)) +} + +func (f *Functions) GetUniformLocation(p Program, name string) Uniform { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + return Uniform{int(C.glGetUniformLocation(&f.f, C.GLuint(p.V), cname))} +} + +func (f *Functions) GetVertexAttrib(index int, pname Enum) int { + C.glGetVertexAttribiv(&f.f, C.GLuint(index), C.GLenum(pname), &f.ints[0]) + return int(f.ints[0]) +} + +func (f *Functions) GetVertexAttribBinding(index int, pname Enum) Object { + return Object{uint(f.GetVertexAttrib(index, pname))} +} + +func (f *Functions) GetVertexAttribPointer(index int, pname Enum) uintptr { + ptr := C.glGetVertexAttribPointerv(&f.f, C.GLuint(index), C.GLenum(pname)) + return uintptr(ptr) +} + +func (f *Functions) InvalidateFramebuffer(target, attachment Enum) { + C.glInvalidateFramebuffer(&f.f, C.GLenum(target), C.GLenum(attachment)) +} + +func (f *Functions) IsEnabled(cap Enum) bool { + return C.glIsEnabled(&f.f, C.GLenum(cap)) == TRUE +} + +func (f *Functions) LinkProgram(p Program) { + C.glLinkProgram(&f.f, C.GLuint(p.V)) +} + +func (f *Functions) PixelStorei(pname Enum, param int) { + C.glPixelStorei(&f.f, C.GLenum(pname), C.GLint(param)) +} + +func (f *Functions) MemoryBarrier(barriers Enum) { + C.glMemoryBarrier(&f.f, C.GLbitfield(barriers)) +} + +func (f *Functions) MapBufferRange(target Enum, offset, length int, access Enum) []byte { + p := C.glMapBufferRange(&f.f, C.GLenum(target), C.GLintptr(offset), C.GLsizeiptr(length), C.GLbitfield(access)) + if p == nil { + return nil + } + return (*[1 << 30]byte)(p)[:length:length] +} + +func (f *Functions) Scissor(x, y, width, height int32) { + C.glScissor(&f.f, C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height)) +} + +func (f *Functions) ReadPixels(x, y, width, height int, format, ty Enum, data []byte) { + var p unsafe.Pointer + if len(data) > 0 { + p = unsafe.Pointer(&data[0]) + } + C.glReadPixels(&f.f, C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height), C.GLenum(format), C.GLenum(ty), p) +} + +func (f *Functions) RenderbufferStorage(target, internalformat Enum, width, height int) { + C.glRenderbufferStorage(&f.f, C.GLenum(target), C.GLenum(internalformat), C.GLsizei(width), C.GLsizei(height)) +} + +func (f *Functions) ShaderSource(s Shader, src string) { + csrc := C.CString(src) + defer C.free(unsafe.Pointer(csrc)) + strlen := C.GLint(len(src)) + C.glShaderSource(&f.f, C.GLuint(s.V), 1, &csrc, &strlen) +} + +func (f *Functions) TexImage2D(target Enum, level int, internalFormat Enum, width int, height int, format Enum, ty Enum) { + C.glTexImage2D(&f.f, C.GLenum(target), C.GLint(level), C.GLint(internalFormat), C.GLsizei(width), C.GLsizei(height), 0, C.GLenum(format), C.GLenum(ty), nil) +} + +func (f *Functions) TexStorage2D(target Enum, levels int, internalFormat Enum, width, height int) { + C.glTexStorage2D(&f.f, C.GLenum(target), C.GLsizei(levels), C.GLenum(internalFormat), C.GLsizei(width), C.GLsizei(height)) +} + +func (f *Functions) TexSubImage2D(target Enum, level int, x int, y int, width int, height int, format Enum, ty Enum, data []byte) { + var p unsafe.Pointer + if len(data) > 0 { + p = unsafe.Pointer(&data[0]) + } + C.glTexSubImage2D(&f.f, C.GLenum(target), C.GLint(level), C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height), C.GLenum(format), C.GLenum(ty), p) +} + +func (f *Functions) TexParameteri(target, pname Enum, param int) { + C.glTexParameteri(&f.f, C.GLenum(target), C.GLenum(pname), C.GLint(param)) +} + +func (f *Functions) UniformBlockBinding(p Program, uniformBlockIndex uint, uniformBlockBinding uint) { + C.glUniformBlockBinding(&f.f, C.GLuint(p.V), C.GLuint(uniformBlockIndex), C.GLuint(uniformBlockBinding)) +} + +func (f *Functions) Uniform1f(dst Uniform, v float32) { + C.glUniform1f(&f.f, C.GLint(dst.V), C.GLfloat(v)) +} + +func (f *Functions) Uniform1i(dst Uniform, v int) { + C.glUniform1i(&f.f, C.GLint(dst.V), C.GLint(v)) +} + +func (f *Functions) Uniform2f(dst Uniform, v0 float32, v1 float32) { + C.glUniform2f(&f.f, C.GLint(dst.V), C.GLfloat(v0), C.GLfloat(v1)) +} + +func (f *Functions) Uniform3f(dst Uniform, v0 float32, v1 float32, v2 float32) { + C.glUniform3f(&f.f, C.GLint(dst.V), C.GLfloat(v0), C.GLfloat(v1), C.GLfloat(v2)) +} + +func (f *Functions) Uniform4f(dst Uniform, v0 float32, v1 float32, v2 float32, v3 float32) { + C.glUniform4f(&f.f, C.GLint(dst.V), C.GLfloat(v0), C.GLfloat(v1), C.GLfloat(v2), C.GLfloat(v3)) +} + +func (f *Functions) UseProgram(p Program) { + C.glUseProgram(&f.f, C.GLuint(p.V)) +} + +func (f *Functions) UnmapBuffer(target Enum) bool { + r := C.glUnmapBuffer(&f.f, C.GLenum(target)) + return r == TRUE +} + +func (f *Functions) VertexAttribPointer(dst Attrib, size int, ty Enum, normalized bool, stride int, offset int) { + var n C.GLboolean = FALSE + if normalized { + n = TRUE + } + C.glVertexAttribPointer(&f.f, C.GLuint(dst), C.GLint(size), C.GLenum(ty), n, C.GLsizei(stride), C.uintptr_t(offset)) +} + +func (f *Functions) Viewport(x int, y int, width int, height int) { + C.glViewport(&f.f, C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height)) +} diff --git a/vendor/gioui.org/internal/gl/gl_windows.go b/vendor/gioui.org/internal/gl/gl_windows.go new file mode 100644 index 0000000..99b1694 --- /dev/null +++ b/vendor/gioui.org/internal/gl/gl_windows.go @@ -0,0 +1,502 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package gl + +import ( + "math" + "runtime" + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var ( + LibGLESv2 = windows.NewLazyDLL("libGLESv2.dll") + _glActiveTexture = LibGLESv2.NewProc("glActiveTexture") + _glAttachShader = LibGLESv2.NewProc("glAttachShader") + _glBeginQuery = LibGLESv2.NewProc("glBeginQuery") + _glBindAttribLocation = LibGLESv2.NewProc("glBindAttribLocation") + _glBindBuffer = LibGLESv2.NewProc("glBindBuffer") + _glBindBufferBase = LibGLESv2.NewProc("glBindBufferBase") + _glBindFramebuffer = LibGLESv2.NewProc("glBindFramebuffer") + _glBindRenderbuffer = LibGLESv2.NewProc("glBindRenderbuffer") + _glBindTexture = LibGLESv2.NewProc("glBindTexture") + _glBindVertexArray = LibGLESv2.NewProc("glBindVertexArray") + _glBlendEquation = LibGLESv2.NewProc("glBlendEquation") + _glBlendFuncSeparate = LibGLESv2.NewProc("glBlendFuncSeparate") + _glBufferData = LibGLESv2.NewProc("glBufferData") + _glBufferSubData = LibGLESv2.NewProc("glBufferSubData") + _glCheckFramebufferStatus = LibGLESv2.NewProc("glCheckFramebufferStatus") + _glClear = LibGLESv2.NewProc("glClear") + _glClearColor = LibGLESv2.NewProc("glClearColor") + _glClearDepthf = LibGLESv2.NewProc("glClearDepthf") + _glDeleteQueries = LibGLESv2.NewProc("glDeleteQueries") + _glDeleteVertexArrays = LibGLESv2.NewProc("glDeleteVertexArrays") + _glCompileShader = LibGLESv2.NewProc("glCompileShader") + _glCopyTexSubImage2D = LibGLESv2.NewProc("glCopyTexSubImage2D") + _glGenBuffers = LibGLESv2.NewProc("glGenBuffers") + _glGenFramebuffers = LibGLESv2.NewProc("glGenFramebuffers") + _glGenVertexArrays = LibGLESv2.NewProc("glGenVertexArrays") + _glGetUniformBlockIndex = LibGLESv2.NewProc("glGetUniformBlockIndex") + _glCreateProgram = LibGLESv2.NewProc("glCreateProgram") + _glGenRenderbuffers = LibGLESv2.NewProc("glGenRenderbuffers") + _glCreateShader = LibGLESv2.NewProc("glCreateShader") + _glGenTextures = LibGLESv2.NewProc("glGenTextures") + _glDeleteBuffers = LibGLESv2.NewProc("glDeleteBuffers") + _glDeleteFramebuffers = LibGLESv2.NewProc("glDeleteFramebuffers") + _glDeleteProgram = LibGLESv2.NewProc("glDeleteProgram") + _glDeleteShader = LibGLESv2.NewProc("glDeleteShader") + _glDeleteRenderbuffers = LibGLESv2.NewProc("glDeleteRenderbuffers") + _glDeleteTextures = LibGLESv2.NewProc("glDeleteTextures") + _glDepthFunc = LibGLESv2.NewProc("glDepthFunc") + _glDepthMask = LibGLESv2.NewProc("glDepthMask") + _glDisableVertexAttribArray = LibGLESv2.NewProc("glDisableVertexAttribArray") + _glDisable = LibGLESv2.NewProc("glDisable") + _glDrawArrays = LibGLESv2.NewProc("glDrawArrays") + _glDrawElements = LibGLESv2.NewProc("glDrawElements") + _glEnable = LibGLESv2.NewProc("glEnable") + _glEnableVertexAttribArray = LibGLESv2.NewProc("glEnableVertexAttribArray") + _glEndQuery = LibGLESv2.NewProc("glEndQuery") + _glFinish = LibGLESv2.NewProc("glFinish") + _glFlush = LibGLESv2.NewProc("glFlush") + _glFramebufferRenderbuffer = LibGLESv2.NewProc("glFramebufferRenderbuffer") + _glFramebufferTexture2D = LibGLESv2.NewProc("glFramebufferTexture2D") + _glGenQueries = LibGLESv2.NewProc("glGenQueries") + _glGetError = LibGLESv2.NewProc("glGetError") + _glGetRenderbufferParameteriv = LibGLESv2.NewProc("glGetRenderbufferParameteriv") + _glGetFloatv = LibGLESv2.NewProc("glGetFloatv") + _glGetFramebufferAttachmentParameteriv = LibGLESv2.NewProc("glGetFramebufferAttachmentParameteriv") + _glGetIntegerv = LibGLESv2.NewProc("glGetIntegerv") + _glGetIntegeri_v = LibGLESv2.NewProc("glGetIntegeri_v") + _glGetProgramiv = LibGLESv2.NewProc("glGetProgramiv") + _glGetProgramInfoLog = LibGLESv2.NewProc("glGetProgramInfoLog") + _glGetQueryObjectuiv = LibGLESv2.NewProc("glGetQueryObjectuiv") + _glGetShaderiv = LibGLESv2.NewProc("glGetShaderiv") + _glGetShaderInfoLog = LibGLESv2.NewProc("glGetShaderInfoLog") + _glGetString = LibGLESv2.NewProc("glGetString") + _glGetUniformLocation = LibGLESv2.NewProc("glGetUniformLocation") + _glGetVertexAttribiv = LibGLESv2.NewProc("glGetVertexAttribiv") + _glGetVertexAttribPointerv = LibGLESv2.NewProc("glGetVertexAttribPointerv") + _glInvalidateFramebuffer = LibGLESv2.NewProc("glInvalidateFramebuffer") + _glIsEnabled = LibGLESv2.NewProc("glIsEnabled") + _glLinkProgram = LibGLESv2.NewProc("glLinkProgram") + _glPixelStorei = LibGLESv2.NewProc("glPixelStorei") + _glReadPixels = LibGLESv2.NewProc("glReadPixels") + _glRenderbufferStorage = LibGLESv2.NewProc("glRenderbufferStorage") + _glScissor = LibGLESv2.NewProc("glScissor") + _glShaderSource = LibGLESv2.NewProc("glShaderSource") + _glTexImage2D = LibGLESv2.NewProc("glTexImage2D") + _glTexStorage2D = LibGLESv2.NewProc("glTexStorage2D") + _glTexSubImage2D = LibGLESv2.NewProc("glTexSubImage2D") + _glTexParameteri = LibGLESv2.NewProc("glTexParameteri") + _glUniformBlockBinding = LibGLESv2.NewProc("glUniformBlockBinding") + _glUniform1f = LibGLESv2.NewProc("glUniform1f") + _glUniform1i = LibGLESv2.NewProc("glUniform1i") + _glUniform2f = LibGLESv2.NewProc("glUniform2f") + _glUniform3f = LibGLESv2.NewProc("glUniform3f") + _glUniform4f = LibGLESv2.NewProc("glUniform4f") + _glUseProgram = LibGLESv2.NewProc("glUseProgram") + _glVertexAttribPointer = LibGLESv2.NewProc("glVertexAttribPointer") + _glViewport = LibGLESv2.NewProc("glViewport") +) + +type Functions struct { + // Query caches. + int32s [100]int32 + float32s [100]float32 + uintptrs [100]uintptr +} + +type Context interface{} + +func NewFunctions(ctx Context, forceES bool) (*Functions, error) { + if ctx != nil { + panic("non-nil context") + } + return new(Functions), nil +} + +func (c *Functions) ActiveTexture(t Enum) { + syscall.Syscall(_glActiveTexture.Addr(), 1, uintptr(t), 0, 0) +} +func (c *Functions) AttachShader(p Program, s Shader) { + syscall.Syscall(_glAttachShader.Addr(), 2, uintptr(p.V), uintptr(s.V), 0) +} +func (f *Functions) BeginQuery(target Enum, query Query) { + syscall.Syscall(_glBeginQuery.Addr(), 2, uintptr(target), uintptr(query.V), 0) +} +func (c *Functions) BindAttribLocation(p Program, a Attrib, name string) { + cname := cString(name) + c0 := &cname[0] + syscall.Syscall(_glBindAttribLocation.Addr(), 3, uintptr(p.V), uintptr(a), uintptr(unsafe.Pointer(c0))) + issue34474KeepAlive(c) +} +func (c *Functions) BindBuffer(target Enum, b Buffer) { + syscall.Syscall(_glBindBuffer.Addr(), 2, uintptr(target), uintptr(b.V), 0) +} +func (c *Functions) BindBufferBase(target Enum, index int, b Buffer) { + syscall.Syscall(_glBindBufferBase.Addr(), 3, uintptr(target), uintptr(index), uintptr(b.V)) +} +func (c *Functions) BindFramebuffer(target Enum, fb Framebuffer) { + syscall.Syscall(_glBindFramebuffer.Addr(), 2, uintptr(target), uintptr(fb.V), 0) +} +func (c *Functions) BindRenderbuffer(target Enum, rb Renderbuffer) { + syscall.Syscall(_glBindRenderbuffer.Addr(), 2, uintptr(target), uintptr(rb.V), 0) +} +func (f *Functions) BindImageTexture(unit int, t Texture, level int, layered bool, layer int, access, format Enum) { + panic("not implemented") +} +func (c *Functions) BindTexture(target Enum, t Texture) { + syscall.Syscall(_glBindTexture.Addr(), 2, uintptr(target), uintptr(t.V), 0) +} +func (c *Functions) BindVertexArray(a VertexArray) { + syscall.Syscall(_glBindVertexArray.Addr(), 1, uintptr(a.V), 0, 0) +} +func (c *Functions) BlendEquation(mode Enum) { + syscall.Syscall(_glBlendEquation.Addr(), 1, uintptr(mode), 0, 0) +} +func (c *Functions) BlendFuncSeparate(srcRGB, dstRGB, srcA, dstA Enum) { + syscall.Syscall6(_glBlendFuncSeparate.Addr(), 4, uintptr(srcRGB), uintptr(dstRGB), uintptr(srcA), uintptr(dstA), 0, 0) +} +func (c *Functions) BufferData(target Enum, size int, usage Enum, data []byte) { + var p unsafe.Pointer + if len(data) > 0 { + p = unsafe.Pointer(&data[0]) + } + syscall.Syscall6(_glBufferData.Addr(), 4, uintptr(target), uintptr(size), uintptr(p), uintptr(usage), 0, 0) +} +func (f *Functions) BufferSubData(target Enum, offset int, src []byte) { + if n := len(src); n > 0 { + s0 := &src[0] + syscall.Syscall6(_glBufferSubData.Addr(), 4, uintptr(target), uintptr(offset), uintptr(n), uintptr(unsafe.Pointer(s0)), 0, 0) + issue34474KeepAlive(s0) + } +} +func (c *Functions) CheckFramebufferStatus(target Enum) Enum { + s, _, _ := syscall.Syscall(_glCheckFramebufferStatus.Addr(), 1, uintptr(target), 0, 0) + return Enum(s) +} +func (c *Functions) Clear(mask Enum) { + syscall.Syscall(_glClear.Addr(), 1, uintptr(mask), 0, 0) +} +func (c *Functions) ClearColor(red, green, blue, alpha float32) { + syscall.Syscall6(_glClearColor.Addr(), 4, uintptr(math.Float32bits(red)), uintptr(math.Float32bits(green)), uintptr(math.Float32bits(blue)), uintptr(math.Float32bits(alpha)), 0, 0) +} +func (c *Functions) ClearDepthf(d float32) { + syscall.Syscall(_glClearDepthf.Addr(), 1, uintptr(math.Float32bits(d)), 0, 0) +} +func (c *Functions) CompileShader(s Shader) { + syscall.Syscall(_glCompileShader.Addr(), 1, uintptr(s.V), 0, 0) +} +func (f *Functions) CopyTexSubImage2D(target Enum, level, xoffset, yoffset, x, y, width, height int) { + syscall.Syscall9(_glCopyTexSubImage2D.Addr(), 8, uintptr(target), uintptr(level), uintptr(xoffset), uintptr(yoffset), uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0) +} +func (c *Functions) CreateBuffer() Buffer { + var buf uintptr + syscall.Syscall(_glGenBuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&buf)), 0) + return Buffer{uint(buf)} +} +func (c *Functions) CreateFramebuffer() Framebuffer { + var fb uintptr + syscall.Syscall(_glGenFramebuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&fb)), 0) + return Framebuffer{uint(fb)} +} +func (c *Functions) CreateProgram() Program { + p, _, _ := syscall.Syscall(_glCreateProgram.Addr(), 0, 0, 0, 0) + return Program{uint(p)} +} +func (f *Functions) CreateQuery() Query { + var q uintptr + syscall.Syscall(_glGenQueries.Addr(), 2, 1, uintptr(unsafe.Pointer(&q)), 0) + return Query{uint(q)} +} +func (c *Functions) CreateRenderbuffer() Renderbuffer { + var rb uintptr + syscall.Syscall(_glGenRenderbuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&rb)), 0) + return Renderbuffer{uint(rb)} +} +func (c *Functions) CreateShader(ty Enum) Shader { + s, _, _ := syscall.Syscall(_glCreateShader.Addr(), 1, uintptr(ty), 0, 0) + return Shader{uint(s)} +} +func (c *Functions) CreateTexture() Texture { + var t uintptr + syscall.Syscall(_glGenTextures.Addr(), 2, 1, uintptr(unsafe.Pointer(&t)), 0) + return Texture{uint(t)} +} +func (c *Functions) CreateVertexArray() VertexArray { + var t uintptr + syscall.Syscall(_glGenVertexArrays.Addr(), 2, 1, uintptr(unsafe.Pointer(&t)), 0) + return VertexArray{uint(t)} +} +func (c *Functions) DeleteBuffer(v Buffer) { + syscall.Syscall(_glDeleteBuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v)), 0) +} +func (c *Functions) DeleteFramebuffer(v Framebuffer) { + syscall.Syscall(_glDeleteFramebuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0) +} +func (c *Functions) DeleteProgram(p Program) { + syscall.Syscall(_glDeleteProgram.Addr(), 1, uintptr(p.V), 0, 0) +} +func (f *Functions) DeleteQuery(query Query) { + syscall.Syscall(_glDeleteQueries.Addr(), 2, 1, uintptr(unsafe.Pointer(&query.V)), 0) +} +func (c *Functions) DeleteShader(s Shader) { + syscall.Syscall(_glDeleteShader.Addr(), 1, uintptr(s.V), 0, 0) +} +func (c *Functions) DeleteRenderbuffer(v Renderbuffer) { + syscall.Syscall(_glDeleteRenderbuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0) +} +func (c *Functions) DeleteTexture(v Texture) { + syscall.Syscall(_glDeleteTextures.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0) +} +func (f *Functions) DeleteVertexArray(array VertexArray) { + syscall.Syscall(_glDeleteVertexArrays.Addr(), 2, 1, uintptr(unsafe.Pointer(&array.V)), 0) +} +func (c *Functions) DepthFunc(f Enum) { + syscall.Syscall(_glDepthFunc.Addr(), 1, uintptr(f), 0, 0) +} +func (c *Functions) DepthMask(mask bool) { + var m uintptr + if mask { + m = 1 + } + syscall.Syscall(_glDepthMask.Addr(), 1, m, 0, 0) +} +func (c *Functions) DisableVertexAttribArray(a Attrib) { + syscall.Syscall(_glDisableVertexAttribArray.Addr(), 1, uintptr(a), 0, 0) +} +func (c *Functions) Disable(cap Enum) { + syscall.Syscall(_glDisable.Addr(), 1, uintptr(cap), 0, 0) +} +func (c *Functions) DrawArrays(mode Enum, first, count int) { + syscall.Syscall(_glDrawArrays.Addr(), 3, uintptr(mode), uintptr(first), uintptr(count)) +} +func (c *Functions) DrawElements(mode Enum, count int, ty Enum, offset int) { + syscall.Syscall6(_glDrawElements.Addr(), 4, uintptr(mode), uintptr(count), uintptr(ty), uintptr(offset), 0, 0) +} +func (f *Functions) DispatchCompute(x, y, z int) { + panic("not implemented") +} +func (c *Functions) Enable(cap Enum) { + syscall.Syscall(_glEnable.Addr(), 1, uintptr(cap), 0, 0) +} +func (c *Functions) EnableVertexAttribArray(a Attrib) { + syscall.Syscall(_glEnableVertexAttribArray.Addr(), 1, uintptr(a), 0, 0) +} +func (f *Functions) EndQuery(target Enum) { + syscall.Syscall(_glEndQuery.Addr(), 1, uintptr(target), 0, 0) +} +func (c *Functions) Finish() { + syscall.Syscall(_glFinish.Addr(), 0, 0, 0, 0) +} +func (c *Functions) Flush() { + syscall.Syscall(_glFlush.Addr(), 0, 0, 0, 0) +} +func (c *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget Enum, renderbuffer Renderbuffer) { + syscall.Syscall6(_glFramebufferRenderbuffer.Addr(), 4, uintptr(target), uintptr(attachment), uintptr(renderbuffertarget), uintptr(renderbuffer.V), 0, 0) +} +func (c *Functions) FramebufferTexture2D(target, attachment, texTarget Enum, t Texture, level int) { + syscall.Syscall6(_glFramebufferTexture2D.Addr(), 5, uintptr(target), uintptr(attachment), uintptr(texTarget), uintptr(t.V), uintptr(level), 0) +} +func (f *Functions) GetUniformBlockIndex(p Program, name string) uint { + cname := cString(name) + c0 := &cname[0] + u, _, _ := syscall.Syscall(_glGetUniformBlockIndex.Addr(), 2, uintptr(p.V), uintptr(unsafe.Pointer(c0)), 0) + issue34474KeepAlive(c0) + return uint(u) +} +func (c *Functions) GetBinding(pname Enum) Object { + return Object{uint(c.GetInteger(pname))} +} +func (c *Functions) GetBindingi(pname Enum, idx int) Object { + return Object{uint(c.GetIntegeri(pname, idx))} +} +func (c *Functions) GetError() Enum { + e, _, _ := syscall.Syscall(_glGetError.Addr(), 0, 0, 0, 0) + return Enum(e) +} +func (c *Functions) GetRenderbufferParameteri(target, pname Enum) int { + syscall.Syscall(_glGetRenderbufferParameteriv.Addr(), 3, uintptr(target), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0]))) + return int(c.int32s[0]) +} +func (c *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname Enum) int { + syscall.Syscall6(_glGetFramebufferAttachmentParameteriv.Addr(), 4, uintptr(target), uintptr(attachment), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0, 0) + return int(c.int32s[0]) +} +func (c *Functions) GetInteger4(pname Enum) [4]int { + syscall.Syscall(_glGetIntegerv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0) + var r [4]int + for i := range r { + r[i] = int(c.int32s[i]) + } + return r +} +func (c *Functions) GetInteger(pname Enum) int { + syscall.Syscall(_glGetIntegerv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0) + return int(c.int32s[0]) +} +func (c *Functions) GetIntegeri(pname Enum, idx int) int { + syscall.Syscall(_glGetIntegeri_v.Addr(), 3, uintptr(pname), uintptr(idx), uintptr(unsafe.Pointer(&c.int32s[0]))) + return int(c.int32s[0]) +} +func (c *Functions) GetFloat(pname Enum) float32 { + syscall.Syscall(_glGetFloatv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.float32s[0])), 0) + return c.float32s[0] +} +func (c *Functions) GetFloat4(pname Enum) [4]float32 { + syscall.Syscall(_glGetFloatv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.float32s[0])), 0) + var r [4]float32 + copy(r[:], c.float32s[:]) + return r +} +func (c *Functions) GetProgrami(p Program, pname Enum) int { + syscall.Syscall(_glGetProgramiv.Addr(), 3, uintptr(p.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0]))) + return int(c.int32s[0]) +} +func (c *Functions) GetProgramInfoLog(p Program) string { + n := c.GetProgrami(p, INFO_LOG_LENGTH) + buf := make([]byte, n) + syscall.Syscall6(_glGetProgramInfoLog.Addr(), 4, uintptr(p.V), uintptr(len(buf)), 0, uintptr(unsafe.Pointer(&buf[0])), 0, 0) + return string(buf) +} +func (c *Functions) GetQueryObjectuiv(query Query, pname Enum) uint { + syscall.Syscall(_glGetQueryObjectuiv.Addr(), 3, uintptr(query.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0]))) + return uint(c.int32s[0]) +} +func (c *Functions) GetShaderi(s Shader, pname Enum) int { + syscall.Syscall(_glGetShaderiv.Addr(), 3, uintptr(s.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0]))) + return int(c.int32s[0]) +} +func (c *Functions) GetShaderInfoLog(s Shader) string { + n := c.GetShaderi(s, INFO_LOG_LENGTH) + buf := make([]byte, n) + syscall.Syscall6(_glGetShaderInfoLog.Addr(), 4, uintptr(s.V), uintptr(len(buf)), 0, uintptr(unsafe.Pointer(&buf[0])), 0, 0) + return string(buf) +} +func (c *Functions) GetString(pname Enum) string { + s, _, _ := syscall.Syscall(_glGetString.Addr(), 1, uintptr(pname), 0, 0) + return windows.BytePtrToString((*byte)(unsafe.Pointer(s))) +} +func (c *Functions) GetUniformLocation(p Program, name string) Uniform { + cname := cString(name) + c0 := &cname[0] + u, _, _ := syscall.Syscall(_glGetUniformLocation.Addr(), 2, uintptr(p.V), uintptr(unsafe.Pointer(c0)), 0) + issue34474KeepAlive(c0) + return Uniform{int(u)} +} +func (c *Functions) GetVertexAttrib(index int, pname Enum) int { + syscall.Syscall(_glGetVertexAttribiv.Addr(), 3, uintptr(index), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0]))) + return int(c.int32s[0]) +} + +func (c *Functions) GetVertexAttribBinding(index int, pname Enum) Object { + return Object{uint(c.GetVertexAttrib(index, pname))} +} + +func (c *Functions) GetVertexAttribPointer(index int, pname Enum) uintptr { + syscall.Syscall(_glGetVertexAttribPointerv.Addr(), 3, uintptr(index), uintptr(pname), uintptr(unsafe.Pointer(&c.uintptrs[0]))) + return c.uintptrs[0] +} +func (c *Functions) InvalidateFramebuffer(target, attachment Enum) { + addr := _glInvalidateFramebuffer.Addr() + if addr == 0 { + // InvalidateFramebuffer is just a hint. Skip it if not supported. + return + } + syscall.Syscall(addr, 3, uintptr(target), 1, uintptr(unsafe.Pointer(&attachment))) +} +func (f *Functions) IsEnabled(cap Enum) bool { + u, _, _ := syscall.Syscall(_glIsEnabled.Addr(), 1, uintptr(cap), 0, 0) + return u == TRUE +} +func (c *Functions) LinkProgram(p Program) { + syscall.Syscall(_glLinkProgram.Addr(), 1, uintptr(p.V), 0, 0) +} +func (c *Functions) PixelStorei(pname Enum, param int) { + syscall.Syscall(_glPixelStorei.Addr(), 2, uintptr(pname), uintptr(param), 0) +} +func (f *Functions) MemoryBarrier(barriers Enum) { + panic("not implemented") +} +func (f *Functions) MapBufferRange(target Enum, offset, length int, access Enum) []byte { + panic("not implemented") +} +func (f *Functions) ReadPixels(x, y, width, height int, format, ty Enum, data []byte) { + d0 := &data[0] + syscall.Syscall9(_glReadPixels.Addr(), 7, uintptr(x), uintptr(y), uintptr(width), uintptr(height), uintptr(format), uintptr(ty), uintptr(unsafe.Pointer(d0)), 0, 0) + issue34474KeepAlive(d0) +} +func (c *Functions) RenderbufferStorage(target, internalformat Enum, width, height int) { + syscall.Syscall6(_glRenderbufferStorage.Addr(), 4, uintptr(target), uintptr(internalformat), uintptr(width), uintptr(height), 0, 0) +} +func (c *Functions) Scissor(x, y, width, height int32) { + syscall.Syscall6(_glScissor.Addr(), 4, uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0, 0) +} +func (c *Functions) ShaderSource(s Shader, src string) { + var n uintptr = uintptr(len(src)) + psrc := &src + syscall.Syscall6(_glShaderSource.Addr(), 4, uintptr(s.V), 1, uintptr(unsafe.Pointer(psrc)), uintptr(unsafe.Pointer(&n)), 0, 0) + issue34474KeepAlive(psrc) +} +func (f *Functions) TexImage2D(target Enum, level int, internalFormat Enum, width int, height int, format Enum, ty Enum) { + syscall.Syscall9(_glTexImage2D.Addr(), 9, uintptr(target), uintptr(level), uintptr(internalFormat), uintptr(width), uintptr(height), 0, uintptr(format), uintptr(ty), 0) +} +func (f *Functions) TexStorage2D(target Enum, levels int, internalFormat Enum, width, height int) { + syscall.Syscall6(_glTexStorage2D.Addr(), 5, uintptr(target), uintptr(levels), uintptr(internalFormat), uintptr(width), uintptr(height), 0) +} +func (c *Functions) TexSubImage2D(target Enum, level int, x, y, width, height int, format, ty Enum, data []byte) { + d0 := &data[0] + syscall.Syscall9(_glTexSubImage2D.Addr(), 9, uintptr(target), uintptr(level), uintptr(x), uintptr(y), uintptr(width), uintptr(height), uintptr(format), uintptr(ty), uintptr(unsafe.Pointer(d0))) + issue34474KeepAlive(d0) +} +func (c *Functions) TexParameteri(target, pname Enum, param int) { + syscall.Syscall(_glTexParameteri.Addr(), 3, uintptr(target), uintptr(pname), uintptr(param)) +} +func (f *Functions) UniformBlockBinding(p Program, uniformBlockIndex uint, uniformBlockBinding uint) { + syscall.Syscall(_glUniformBlockBinding.Addr(), 3, uintptr(p.V), uintptr(uniformBlockIndex), uintptr(uniformBlockBinding)) +} +func (c *Functions) Uniform1f(dst Uniform, v float32) { + syscall.Syscall(_glUniform1f.Addr(), 2, uintptr(dst.V), uintptr(math.Float32bits(v)), 0) +} +func (c *Functions) Uniform1i(dst Uniform, v int) { + syscall.Syscall(_glUniform1i.Addr(), 2, uintptr(dst.V), uintptr(v), 0) +} +func (c *Functions) Uniform2f(dst Uniform, v0, v1 float32) { + syscall.Syscall(_glUniform2f.Addr(), 3, uintptr(dst.V), uintptr(math.Float32bits(v0)), uintptr(math.Float32bits(v1))) +} +func (c *Functions) Uniform3f(dst Uniform, v0, v1, v2 float32) { + syscall.Syscall6(_glUniform3f.Addr(), 4, uintptr(dst.V), uintptr(math.Float32bits(v0)), uintptr(math.Float32bits(v1)), uintptr(math.Float32bits(v2)), 0, 0) +} +func (c *Functions) Uniform4f(dst Uniform, v0, v1, v2, v3 float32) { + syscall.Syscall6(_glUniform4f.Addr(), 5, uintptr(dst.V), uintptr(math.Float32bits(v0)), uintptr(math.Float32bits(v1)), uintptr(math.Float32bits(v2)), uintptr(math.Float32bits(v3)), 0) +} +func (c *Functions) UseProgram(p Program) { + syscall.Syscall(_glUseProgram.Addr(), 1, uintptr(p.V), 0, 0) +} +func (f *Functions) UnmapBuffer(target Enum) bool { + panic("not implemented") +} +func (c *Functions) VertexAttribPointer(dst Attrib, size int, ty Enum, normalized bool, stride, offset int) { + var norm uintptr + if normalized { + norm = 1 + } + syscall.Syscall6(_glVertexAttribPointer.Addr(), 6, uintptr(dst), uintptr(size), uintptr(ty), norm, uintptr(stride), uintptr(offset)) +} +func (c *Functions) Viewport(x, y, width, height int) { + syscall.Syscall6(_glViewport.Addr(), 4, uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0, 0) +} + +func cString(s string) []byte { + b := make([]byte, len(s)+1) + copy(b, s) + return b +} + +// issue34474KeepAlive calls runtime.KeepAlive as a +// workaround for golang.org/issue/34474. +func issue34474KeepAlive(v interface{}) { + runtime.KeepAlive(v) +} diff --git a/vendor/gioui.org/internal/gl/types.go b/vendor/gioui.org/internal/gl/types.go new file mode 100644 index 0000000..dd24963 --- /dev/null +++ b/vendor/gioui.org/internal/gl/types.go @@ -0,0 +1,77 @@ +//go:build !js +// +build !js + +package gl + +type ( + Object struct{ V uint } + Buffer Object + Framebuffer Object + Program Object + Renderbuffer Object + Shader Object + Texture Object + Query Object + Uniform struct{ V int } + VertexArray Object +) + +func (o Object) valid() bool { + return o.V != 0 +} + +func (o Object) equal(o2 Object) bool { + return o == o2 +} + +func (u Framebuffer) Valid() bool { + return Object(u).valid() +} + +func (u Uniform) Valid() bool { + return u.V != -1 +} + +func (p Program) Valid() bool { + return Object(p).valid() +} + +func (s Shader) Valid() bool { + return Object(s).valid() +} + +func (a VertexArray) Valid() bool { + return Object(a).valid() +} + +func (f Framebuffer) Equal(f2 Framebuffer) bool { + return Object(f).equal(Object(f2)) +} + +func (p Program) Equal(p2 Program) bool { + return Object(p).equal(Object(p2)) +} + +func (s Shader) Equal(s2 Shader) bool { + return Object(s).equal(Object(s2)) +} + +func (u Uniform) Equal(u2 Uniform) bool { + return u == u2 +} + +func (a VertexArray) Equal(a2 VertexArray) bool { + return Object(a).equal(Object(a2)) +} + +func (r Renderbuffer) Equal(r2 Renderbuffer) bool { + return Object(r).equal(Object(r2)) +} + +func (t Texture) Equal(t2 Texture) bool { + return Object(t).equal(Object(t2)) +} + +func (b Buffer) Equal(b2 Buffer) bool { + return Object(b).equal(Object(b2)) +} diff --git a/vendor/gioui.org/internal/gl/types_js.go b/vendor/gioui.org/internal/gl/types_js.go new file mode 100644 index 0000000..8d91a6b --- /dev/null +++ b/vendor/gioui.org/internal/gl/types_js.go @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package gl + +import "syscall/js" + +type ( + Object js.Value + Buffer Object + Framebuffer Object + Program Object + Renderbuffer Object + Shader Object + Texture Object + Query Object + Uniform Object + VertexArray Object +) + +func (o Object) valid() bool { + return js.Value(o).Truthy() +} + +func (o Object) equal(o2 Object) bool { + return js.Value(o).Equal(js.Value(o2)) +} + +func (b Buffer) Valid() bool { + return Object(b).valid() +} + +func (f Framebuffer) Valid() bool { + return Object(f).valid() +} + +func (p Program) Valid() bool { + return Object(p).valid() +} + +func (r Renderbuffer) Valid() bool { + return Object(r).valid() +} + +func (s Shader) Valid() bool { + return Object(s).valid() +} + +func (t Texture) Valid() bool { + return Object(t).valid() +} + +func (u Uniform) Valid() bool { + return Object(u).valid() +} + +func (a VertexArray) Valid() bool { + return Object(a).valid() +} + +func (f Framebuffer) Equal(f2 Framebuffer) bool { + return Object(f).equal(Object(f2)) +} + +func (p Program) Equal(p2 Program) bool { + return Object(p).equal(Object(p2)) +} + +func (s Shader) Equal(s2 Shader) bool { + return Object(s).equal(Object(s2)) +} + +func (u Uniform) Equal(u2 Uniform) bool { + return Object(u).equal(Object(u2)) +} + +func (a VertexArray) Equal(a2 VertexArray) bool { + return Object(a).equal(Object(a2)) +} + +func (r Renderbuffer) Equal(r2 Renderbuffer) bool { + return Object(r).equal(Object(r2)) +} + +func (t Texture) Equal(t2 Texture) bool { + return Object(t).equal(Object(t2)) +} + +func (b Buffer) Equal(b2 Buffer) bool { + return Object(b).equal(Object(b2)) +} diff --git a/vendor/gioui.org/gpu/gl/util.go b/vendor/gioui.org/internal/gl/util.go index 65b2155..c696b69 100644 --- a/vendor/gioui.org/gpu/gl/util.go +++ b/vendor/gioui.org/internal/gl/util.go @@ -8,19 +8,19 @@ import ( "strings" ) -func CreateProgram(ctx Functions, vsSrc, fsSrc string, attribs []string) (Program, error) { - vs, err := createShader(ctx, VERTEX_SHADER, vsSrc) +func CreateProgram(ctx *Functions, vsSrc, fsSrc string, attribs []string) (Program, error) { + vs, err := CreateShader(ctx, VERTEX_SHADER, vsSrc) if err != nil { return Program{}, err } defer ctx.DeleteShader(vs) - fs, err := createShader(ctx, FRAGMENT_SHADER, fsSrc) + fs, err := CreateShader(ctx, FRAGMENT_SHADER, fsSrc) if err != nil { return Program{}, err } defer ctx.DeleteShader(fs) prog := ctx.CreateProgram() - if !prog.valid() { + if !prog.Valid() { return Program{}, errors.New("glCreateProgram failed") } ctx.AttachShader(prog, vs) @@ -37,17 +37,29 @@ func CreateProgram(ctx Functions, vsSrc, fsSrc string, attribs []string) (Progra return prog, nil } -func GetUniformLocation(ctx Functions, prog Program, name string) Uniform { - loc := ctx.GetUniformLocation(prog, name) - if !loc.valid() { - panic(fmt.Errorf("uniform %s not found", name)) +func CreateComputeProgram(ctx *Functions, src string) (Program, error) { + cs, err := CreateShader(ctx, COMPUTE_SHADER, src) + if err != nil { + return Program{}, err + } + defer ctx.DeleteShader(cs) + prog := ctx.CreateProgram() + if !prog.Valid() { + return Program{}, errors.New("glCreateProgram failed") + } + ctx.AttachShader(prog, cs) + ctx.LinkProgram(prog) + if ctx.GetProgrami(prog, LINK_STATUS) == 0 { + log := ctx.GetProgramInfoLog(prog) + ctx.DeleteProgram(prog) + return Program{}, fmt.Errorf("program link failed: %s", strings.TrimSpace(log)) } - return loc + return prog, nil } -func createShader(ctx Functions, typ Enum, src string) (Shader, error) { +func CreateShader(ctx *Functions, typ Enum, src string) (Shader, error) { sh := ctx.CreateShader(typ) - if !sh.valid() { + if !sh.Valid() { return Shader{}, errors.New("glCreateShader failed") } ctx.ShaderSource(sh, src) diff --git a/vendor/gioui.org/internal/opconst/ops.go b/vendor/gioui.org/internal/opconst/ops.go deleted file mode 100644 index 62365b8..0000000 --- a/vendor/gioui.org/internal/opconst/ops.go +++ /dev/null @@ -1,84 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package opconst - -type OpType byte - -// Start at a high number for easier debugging. -const firstOpIndex = 200 - -const ( - TypeMacro OpType = iota + firstOpIndex - TypeCall - TypeTransform - TypeLayer - TypeInvalidate - TypeImage - TypePaint - TypeColor - TypeArea - TypePointerInput - TypePass - TypeKeyInput - TypeHideInput - TypePush - TypePop - TypeAux - TypeClip - TypeProfile -) - -const ( - TypeMacroLen = 1 + 4 + 4 - TypeCallLen = 1 + 4 + 4 - TypeTransformLen = 1 + 4*6 - TypeLayerLen = 1 - TypeRedrawLen = 1 + 8 - TypeImageLen = 1 + 4*4 - TypePaintLen = 1 + 4*4 - TypeColorLen = 1 + 4 - TypeAreaLen = 1 + 1 + 4*4 - TypePointerInputLen = 1 + 1 + 1 - TypePassLen = 1 + 1 - TypeKeyInputLen = 1 + 1 - TypeHideInputLen = 1 - TypePushLen = 1 - TypePopLen = 1 - TypeAuxLen = 1 - TypeClipLen = 1 + 4*4 - TypeProfileLen = 1 -) - -func (t OpType) Size() int { - return [...]int{ - TypeMacroLen, - TypeCallLen, - TypeTransformLen, - TypeLayerLen, - TypeRedrawLen, - TypeImageLen, - TypePaintLen, - TypeColorLen, - TypeAreaLen, - TypePointerInputLen, - TypePassLen, - TypeKeyInputLen, - TypeHideInputLen, - TypePushLen, - TypePopLen, - TypeAuxLen, - TypeClipLen, - TypeProfileLen, - }[t-firstOpIndex] -} - -func (t OpType) NumRefs() int { - switch t { - case TypeKeyInput, TypePointerInput, TypeProfile, TypeCall: - return 1 - case TypeImage: - return 2 - default: - return 0 - } -} diff --git a/vendor/gioui.org/internal/ops/ops.go b/vendor/gioui.org/internal/ops/ops.go index c56b9d6..fece6d6 100644 --- a/vendor/gioui.org/internal/ops/ops.go +++ b/vendor/gioui.org/internal/ops/ops.go @@ -4,57 +4,321 @@ package ops import ( "encoding/binary" + "image" "math" "gioui.org/f32" - "gioui.org/internal/opconst" + "gioui.org/internal/byteslice" + "gioui.org/internal/scene" ) -const QuadSize = 4 * 2 * 3 +type Ops struct { + // version is incremented at each Reset. + version int + // data contains the serialized operations. + data []byte + // refs hold external references for operations. + refs []interface{} + // nextStateID is the id allocated for the next + // StateOp. + nextStateID int -type Quad struct { - From, Ctrl, To f32.Point + macroStack stack + stacks [5]stack } -func (q Quad) Transform(t f32.Affine2D) Quad { - q.From = t.Transform(q.From) - q.Ctrl = t.Transform(q.Ctrl) - q.To = t.Transform(q.To) - return q +type OpType byte + +type Shape byte + +// Start at a high number for easier debugging. +const firstOpIndex = 200 + +const ( + TypeMacro OpType = iota + firstOpIndex + TypeCall + TypeDefer + TypePushTransform + TypeTransform + TypePopTransform + TypeInvalidate + TypeImage + TypePaint + TypeColor + TypeLinearGradient + TypePass + TypePopPass + TypePointerInput + TypeClipboardRead + TypeClipboardWrite + TypeSource + TypeTarget + TypeOffer + TypeKeyInput + TypeKeyFocus + TypeKeySoftKeyboard + TypeSave + TypeLoad + TypeAux + TypeClip + TypePopClip + TypeProfile + TypeCursor + TypePath + TypeStroke + TypeSemanticLabel + TypeSemanticDesc + TypeSemanticClass + TypeSemanticSelected + TypeSemanticDisabled +) + +type StackID struct { + id int + prev int +} + +// StateOp represents a saved operation snapshop to be restored +// later. +type StateOp struct { + id int + macroID int + ops *Ops +} + +// stack tracks the integer identities of stack operations to ensure correct +// pairing of their push and pop methods. +type stack struct { + currentID int + nextID int +} + +type StackKind uint8 + +// ClipOp is the shadow of clip.Op. +type ClipOp struct { + Bounds image.Rectangle + Outline bool + Shape Shape } -func EncodeQuad(d []byte, q Quad) { +const ( + ClipStack StackKind = iota + TransStack + PassStack + MetaStack +) + +const ( + Path Shape = iota + Ellipse + Rect +) + +const ( + TypeMacroLen = 1 + 4 + 4 + TypeCallLen = 1 + 4 + 4 + TypeDeferLen = 1 + TypePushTransformLen = 1 + 4*6 + TypeTransformLen = 1 + 1 + 4*6 + TypePopTransformLen = 1 + TypeRedrawLen = 1 + 8 + TypeImageLen = 1 + TypePaintLen = 1 + TypeColorLen = 1 + 4 + TypeLinearGradientLen = 1 + 8*2 + 4*2 + TypePassLen = 1 + TypePopPassLen = 1 + TypePointerInputLen = 1 + 1 + 1*2 + 2*4 + 2*4 + TypeClipboardReadLen = 1 + TypeClipboardWriteLen = 1 + TypeSourceLen = 1 + TypeTargetLen = 1 + TypeOfferLen = 1 + TypeKeyInputLen = 1 + 1 + TypeKeyFocusLen = 1 + 1 + TypeKeySoftKeyboardLen = 1 + 1 + TypeSaveLen = 1 + 4 + TypeLoadLen = 1 + 4 + TypeAuxLen = 1 + TypeClipLen = 1 + 4*4 + 1 + 1 + TypePopClipLen = 1 + TypeProfileLen = 1 + TypeCursorLen = 1 + 1 + TypePathLen = 8 + 1 + TypeStrokeLen = 1 + 4 + TypeSemanticLabelLen = 1 + TypeSemanticDescLen = 1 + TypeSemanticClassLen = 2 + TypeSemanticSelectedLen = 2 + TypeSemanticDisabledLen = 2 +) + +func (op *ClipOp) Decode(data []byte) { + if OpType(data[0]) != TypeClip { + panic("invalid op") + } bo := binary.LittleEndian - bo.PutUint32(d[0:], math.Float32bits(q.From.X)) - bo.PutUint32(d[4:], math.Float32bits(q.From.Y)) - bo.PutUint32(d[8:], math.Float32bits(q.Ctrl.X)) - bo.PutUint32(d[12:], math.Float32bits(q.Ctrl.Y)) - bo.PutUint32(d[16:], math.Float32bits(q.To.X)) - bo.PutUint32(d[20:], math.Float32bits(q.To.Y)) + r := image.Rectangle{ + Min: image.Point{ + X: int(int32(bo.Uint32(data[1:]))), + Y: int(int32(bo.Uint32(data[5:]))), + }, + Max: image.Point{ + X: int(int32(bo.Uint32(data[9:]))), + Y: int(int32(bo.Uint32(data[13:]))), + }, + } + *op = ClipOp{ + Bounds: r, + Outline: data[17] == 1, + Shape: Shape(data[18]), + } } -func DecodeQuad(d []byte) (q Quad) { +func Reset(o *Ops) { + o.macroStack = stack{} + for i := range o.stacks { + o.stacks[i] = stack{} + } + // Leave references to the GC. + for i := range o.refs { + o.refs[i] = nil + } + o.data = o.data[:0] + o.refs = o.refs[:0] + o.nextStateID = 0 + o.version++ +} + +func Write(o *Ops, n int) []byte { + o.data = append(o.data, make([]byte, n)...) + return o.data[len(o.data)-n:] +} + +func PushMacro(o *Ops) StackID { + return o.macroStack.push() +} + +func PopMacro(o *Ops, id StackID) { + o.macroStack.pop(id) +} + +func FillMacro(o *Ops, startPC PC) { + pc := PCFor(o) + // Fill out the macro definition reserved in Record. + data := o.data[startPC.data:] + data = data[:TypeMacroLen] + data[0] = byte(TypeMacro) bo := binary.LittleEndian - q.From.X = math.Float32frombits(bo.Uint32(d[0:])) - q.From.Y = math.Float32frombits(bo.Uint32(d[4:])) - q.Ctrl.X = math.Float32frombits(bo.Uint32(d[8:])) - q.Ctrl.Y = math.Float32frombits(bo.Uint32(d[12:])) - q.To.X = math.Float32frombits(bo.Uint32(d[16:])) - q.To.Y = math.Float32frombits(bo.Uint32(d[20:])) - return + bo.PutUint32(data[1:], uint32(pc.data)) + bo.PutUint32(data[5:], uint32(pc.refs)) } -func DecodeTransform(d []byte) (t f32.Affine2D) { - if opconst.OpType(d[0]) != opconst.TypeTransform { - panic("invalid op") +func AddCall(o *Ops, callOps *Ops, pc PC) { + data := Write1(o, TypeCallLen, callOps) + data[0] = byte(TypeCall) + bo := binary.LittleEndian + bo.PutUint32(data[1:], uint32(pc.data)) + bo.PutUint32(data[5:], uint32(pc.refs)) +} + +func PushOp(o *Ops, kind StackKind) (StackID, int) { + return o.stacks[kind].push(), o.macroStack.currentID +} + +func PopOp(o *Ops, kind StackKind, sid StackID, macroID int) { + if o.macroStack.currentID != macroID { + panic("stack push and pop must not cross macro boundary") } - if len(d) < 1+6*4 { - panic("too short buffer") + o.stacks[kind].pop(sid) +} + +func Write1(o *Ops, n int, ref1 interface{}) []byte { + o.data = append(o.data, make([]byte, n)...) + o.refs = append(o.refs, ref1) + return o.data[len(o.data)-n:] +} + +func Write2(o *Ops, n int, ref1, ref2 interface{}) []byte { + o.data = append(o.data, make([]byte, n)...) + o.refs = append(o.refs, ref1, ref2) + return o.data[len(o.data)-n:] +} + +func Write3(o *Ops, n int, ref1, ref2, ref3 interface{}) []byte { + o.data = append(o.data, make([]byte, n)...) + o.refs = append(o.refs, ref1, ref2, ref3) + return o.data[len(o.data)-n:] +} + +func PCFor(o *Ops) PC { + return PC{data: len(o.data), refs: len(o.refs)} +} + +func (s *stack) push() StackID { + s.nextID++ + sid := StackID{ + id: s.nextID, + prev: s.currentID, } - return decodeAffine2D(d[1:]) + s.currentID = s.nextID + return sid +} + +func (s *stack) check(sid StackID) { + if s.currentID != sid.id { + panic("unbalanced operation") + } +} + +func (s *stack) pop(sid StackID) { + s.check(sid) + s.currentID = sid.prev +} + +// Save the effective transformation. +func Save(o *Ops) StateOp { + o.nextStateID++ + s := StateOp{ + ops: o, + id: o.nextStateID, + macroID: o.macroStack.currentID, + } + bo := binary.LittleEndian + data := Write(o, TypeSaveLen) + data[0] = byte(TypeSave) + bo.PutUint32(data[1:], uint32(s.id)) + return s +} + +// Load a previously saved operations state given +// its ID. +func (s StateOp) Load() { + bo := binary.LittleEndian + data := Write(s.ops, TypeLoadLen) + data[0] = byte(TypeLoad) + bo.PutUint32(data[1:], uint32(s.id)) +} + +func DecodeCommand(d []byte) scene.Command { + var cmd scene.Command + copy(byteslice.Uint32(cmd[:]), d) + return cmd +} + +func EncodeCommand(out []byte, cmd scene.Command) { + copy(out, byteslice.Uint32(cmd[:])) } -func decodeAffine2D(data []byte) f32.Affine2D { +func DecodeTransform(data []byte) (t f32.Affine2D, push bool) { + if OpType(data[0]) != TypeTransform { + panic("invalid op") + } + push = data[1] != 0 + data = data[2:] + data = data[:4*6] + bo := binary.LittleEndian a := math.Float32frombits(bo.Uint32(data)) b := math.Float32frombits(bo.Uint32(data[4*1:])) @@ -62,5 +326,148 @@ func decodeAffine2D(data []byte) f32.Affine2D { d := math.Float32frombits(bo.Uint32(data[4*3:])) e := math.Float32frombits(bo.Uint32(data[4*4:])) f := math.Float32frombits(bo.Uint32(data[4*5:])) - return f32.NewAffine2D(a, b, c, d, e, f) + return f32.NewAffine2D(a, b, c, d, e, f), push +} + +// DecodeSave decodes the state id of a save op. +func DecodeSave(data []byte) int { + if OpType(data[0]) != TypeSave { + panic("invalid op") + } + bo := binary.LittleEndian + return int(bo.Uint32(data[1:])) +} + +// DecodeLoad decodes the state id of a load op. +func DecodeLoad(data []byte) int { + if OpType(data[0]) != TypeLoad { + panic("invalid op") + } + bo := binary.LittleEndian + return int(bo.Uint32(data[1:])) +} + +func (t OpType) Size() int { + return [...]int{ + TypeMacroLen, + TypeCallLen, + TypeDeferLen, + TypePushTransformLen, + TypeTransformLen, + TypePopTransformLen, + TypeRedrawLen, + TypeImageLen, + TypePaintLen, + TypeColorLen, + TypeLinearGradientLen, + TypePassLen, + TypePopPassLen, + TypePointerInputLen, + TypeClipboardReadLen, + TypeClipboardWriteLen, + TypeSourceLen, + TypeTargetLen, + TypeOfferLen, + TypeKeyInputLen, + TypeKeyFocusLen, + TypeKeySoftKeyboardLen, + TypeSaveLen, + TypeLoadLen, + TypeAuxLen, + TypeClipLen, + TypePopClipLen, + TypeProfileLen, + TypeCursorLen, + TypePathLen, + TypeStrokeLen, + TypeSemanticLabelLen, + TypeSemanticDescLen, + TypeSemanticClassLen, + TypeSemanticSelectedLen, + TypeSemanticDisabledLen, + }[t-firstOpIndex] +} + +func (t OpType) NumRefs() int { + switch t { + case TypeKeyInput, TypeKeyFocus, TypePointerInput, TypeProfile, TypeCall, TypeClipboardRead, TypeClipboardWrite, TypeCursor, TypeSemanticLabel, TypeSemanticDesc: + return 1 + case TypeImage, TypeSource, TypeTarget: + return 2 + case TypeOffer: + return 3 + default: + return 0 + } +} + +func (t OpType) String() string { + switch t { + case TypeMacro: + return "Macro" + case TypeCall: + return "Call" + case TypeDefer: + return "Defer" + case TypePushTransform: + return "PushTransform" + case TypeTransform: + return "Transform" + case TypePopTransform: + return "PopTransform" + case TypeInvalidate: + return "Invalidate" + case TypeImage: + return "Image" + case TypePaint: + return "Paint" + case TypeColor: + return "Color" + case TypeLinearGradient: + return "LinearGradient" + case TypePass: + return "Pass" + case TypePopPass: + return "PopPass" + case TypePointerInput: + return "PointerInput" + case TypeClipboardRead: + return "ClipboardRead" + case TypeClipboardWrite: + return "ClipboardWrite" + case TypeSource: + return "Source" + case TypeTarget: + return "Target" + case TypeOffer: + return "Offer" + case TypeKeyInput: + return "KeyInput" + case TypeKeyFocus: + return "KeyFocus" + case TypeKeySoftKeyboard: + return "KeySoftKeyboard" + case TypeSave: + return "Save" + case TypeLoad: + return "Load" + case TypeAux: + return "Aux" + case TypeClip: + return "Clip" + case TypePopClip: + return "PopClip" + case TypeProfile: + return "Profile" + case TypeCursor: + return "Cursor" + case TypePath: + return "Path" + case TypeStroke: + return "Stroke" + case TypeSemanticLabel: + return "SemanticDescription" + default: + panic("unknown OpType") + } } diff --git a/vendor/gioui.org/internal/ops/reader.go b/vendor/gioui.org/internal/ops/reader.go index 5d713db..99b8cb6 100644 --- a/vendor/gioui.org/internal/ops/reader.go +++ b/vendor/gioui.org/internal/ops/reader.go @@ -4,17 +4,15 @@ package ops import ( "encoding/binary" - - "gioui.org/f32" - "gioui.org/internal/opconst" - "gioui.org/op" ) // Reader parses an ops list. type Reader struct { - pc pc - stack []macro - ops *op.Ops + pc PC + stack []macro + ops *Ops + deferOps Ops + deferDone bool } // EncodedOp represents an encoded op returned by @@ -27,53 +25,52 @@ type EncodedOp struct { // Key is a unique key for a given op. type Key struct { - ops *op.Ops - pc int - version int - sx, hx, sy, hy float32 + ops *Ops + pc int + version int } // Shadow of op.MacroOp. type macroOp struct { - ops *op.Ops - pc pc + ops *Ops + pc PC } -type pc struct { +// PC is an instruction counter for an operation list. +type PC struct { data int refs int } type macro struct { - ops *op.Ops - retPC pc - endPC pc + ops *Ops + retPC PC + endPC PC } type opMacroDef struct { - endpc pc + endpc PC } -// Reset start reading from the op list. -func (r *Reader) Reset(ops *op.Ops) { - r.stack = r.stack[:0] - r.pc = pc{} - r.ops = ops +// Reset start reading from the beginning of ops. +func (r *Reader) Reset(ops *Ops) { + r.ResetAt(ops, PC{}) } -func (k Key) SetTransform(t f32.Affine2D) Key { - sx, hx, _, hy, sy, _ := t.Elems() - k.sx = sx - k.hx = hx - k.hy = hy - k.sy = sy - return k +// ResetAt is like Reset, except it starts reading from pc. +func (r *Reader) ResetAt(ops *Ops, pc PC) { + r.stack = r.stack[:0] + Reset(&r.deferOps) + r.deferDone = false + r.pc = pc + r.ops = ops } func (r *Reader) Decode() (EncodedOp, bool) { if r.ops == nil { return EncodedOp{}, false } + deferring := false for { if len(r.stack) > 0 { b := r.stack[len(r.stack)-1] @@ -84,35 +81,59 @@ func (r *Reader) Decode() (EncodedOp, bool) { continue } } - data := r.ops.Data() + data := r.ops.data data = data[r.pc.data:] + refs := r.ops.refs if len(data) == 0 { - return EncodedOp{}, false + if r.deferDone { + return EncodedOp{}, false + } + r.deferDone = true + // Execute deferred macros. + r.ops = &r.deferOps + r.pc = PC{} + continue } - key := Key{ops: r.ops, pc: r.pc.data, version: r.ops.Version()} - t := opconst.OpType(data[0]) + key := Key{ops: r.ops, pc: r.pc.data, version: r.ops.version} + t := OpType(data[0]) n := t.Size() nrefs := t.NumRefs() data = data[:n] - refs := r.ops.Refs() refs = refs[r.pc.refs:] refs = refs[:nrefs] switch t { - case opconst.TypeAux: + case TypeDefer: + deferring = true + r.pc.data += n + r.pc.refs += nrefs + continue + case TypeAux: // An Aux operations is always wrapped in a macro, and // its length is the remaining space. block := r.stack[len(r.stack)-1] - n += block.endPC.data - r.pc.data - opconst.TypeAuxLen + n += block.endPC.data - r.pc.data - TypeAuxLen data = data[:n] - case opconst.TypeCall: + case TypeCall: + if deferring { + deferring = false + // Copy macro for deferred execution. + if t.NumRefs() != 1 { + panic("internal error: unexpected number of macro refs") + } + deferData := Write1(&r.deferOps, t.Size(), refs[0]) + copy(deferData, data) + r.pc.data += n + r.pc.refs += nrefs + continue + } var op macroOp op.decode(data, refs) - macroData := op.ops.Data()[op.pc.data:] - if opconst.OpType(macroData[0]) != opconst.TypeMacro { + macroData := op.ops.data[op.pc.data:] + if OpType(macroData[0]) != TypeMacro { panic("invalid macro reference") } var opDef opMacroDef - opDef.decode(macroData[:opconst.TypeMacro.Size()]) + opDef.decode(macroData[:TypeMacro.Size()]) retPC := r.pc retPC.data += n retPC.refs += nrefs @@ -123,10 +144,10 @@ func (r *Reader) Decode() (EncodedOp, bool) { }) r.ops = op.ops r.pc = op.pc - r.pc.data += opconst.TypeMacro.Size() - r.pc.refs += opconst.TypeMacro.NumRefs() + r.pc.data += TypeMacro.Size() + r.pc.refs += TypeMacro.NumRefs() continue - case opconst.TypeMacro: + case TypeMacro: var op opMacroDef op.decode(data) r.pc = op.endpc @@ -139,14 +160,15 @@ func (r *Reader) Decode() (EncodedOp, bool) { } func (op *opMacroDef) decode(data []byte) { - if opconst.OpType(data[0]) != opconst.TypeMacro { + if OpType(data[0]) != TypeMacro { panic("invalid op") } bo := binary.LittleEndian + data = data[:9] dataIdx := int(int32(bo.Uint32(data[1:]))) refsIdx := int(int32(bo.Uint32(data[5:]))) *op = opMacroDef{ - endpc: pc{ + endpc: PC{ data: dataIdx, refs: refsIdx, }, @@ -154,15 +176,16 @@ func (op *opMacroDef) decode(data []byte) { } func (m *macroOp) decode(data []byte, refs []interface{}) { - if opconst.OpType(data[0]) != opconst.TypeCall { + if OpType(data[0]) != TypeCall { panic("invalid op") } + data = data[:9] bo := binary.LittleEndian dataIdx := int(int32(bo.Uint32(data[1:]))) refsIdx := int(int32(bo.Uint32(data[5:]))) *m = macroOp{ - ops: refs[0].(*op.Ops), - pc: pc{ + ops: refs[0].(*Ops), + pc: PC{ data: dataIdx, refs: refsIdx, }, diff --git a/vendor/gioui.org/internal/scene/scene.go b/vendor/gioui.org/internal/scene/scene.go new file mode 100644 index 0000000..0358858 --- /dev/null +++ b/vendor/gioui.org/internal/scene/scene.go @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +// Package scene encodes and decodes graphics commands in the format used by the +// compute renderer. +package scene + +import ( + "fmt" + "image" + "image/color" + "math" + "unsafe" + + "gioui.org/f32" +) + +type Op uint32 + +type Command [sceneElemSize / 4]uint32 + +// GPU commands from piet/scene.h in package gioui.org/shaders. +const ( + OpNop Op = iota + OpLine + OpQuad + OpCubic + OpFillColor + OpLineWidth + OpTransform + OpBeginClip + OpEndClip + OpFillImage + OpSetFillMode + OpGap +) + +// FillModes, from setup.h. +type FillMode uint32 + +const ( + FillModeNonzero = 0 + FillModeStroke = 1 +) + +const CommandSize = int(unsafe.Sizeof(Command{})) + +const sceneElemSize = 36 + +func (c Command) Op() Op { + return Op(c[0]) +} + +func (c Command) String() string { + switch Op(c[0]) { + case OpNop: + return "nop" + case OpLine: + from, to := DecodeLine(c) + return fmt.Sprintf("line(%v, %v)", from, to) + case OpGap: + from, to := DecodeLine(c) + return fmt.Sprintf("gap(%v, %v)", from, to) + case OpQuad: + from, ctrl, to := DecodeQuad(c) + return fmt.Sprintf("quad(%v, %v, %v)", from, ctrl, to) + case OpCubic: + from, ctrl0, ctrl1, to := DecodeCubic(c) + return fmt.Sprintf("cubic(%v, %v, %v, %v)", from, ctrl0, ctrl1, to) + case OpFillColor: + return fmt.Sprintf("fillcolor %#.8x", c[1]) + case OpLineWidth: + return "linewidth" + case OpTransform: + t := f32.NewAffine2D( + math.Float32frombits(c[1]), + math.Float32frombits(c[3]), + math.Float32frombits(c[5]), + math.Float32frombits(c[2]), + math.Float32frombits(c[4]), + math.Float32frombits(c[6]), + ) + return fmt.Sprintf("transform (%v)", t) + case OpBeginClip: + bounds := f32.Rectangle{ + Min: f32.Pt(math.Float32frombits(c[1]), math.Float32frombits(c[2])), + Max: f32.Pt(math.Float32frombits(c[3]), math.Float32frombits(c[4])), + } + return fmt.Sprintf("beginclip (%v)", bounds) + case OpEndClip: + bounds := f32.Rectangle{ + Min: f32.Pt(math.Float32frombits(c[1]), math.Float32frombits(c[2])), + Max: f32.Pt(math.Float32frombits(c[3]), math.Float32frombits(c[4])), + } + return fmt.Sprintf("endclip (%v)", bounds) + case OpFillImage: + return "fillimage" + case OpSetFillMode: + return "setfillmode" + default: + panic("unreachable") + } +} + +func Line(start, end f32.Point) Command { + return Command{ + 0: uint32(OpLine), + 1: math.Float32bits(start.X), + 2: math.Float32bits(start.Y), + 3: math.Float32bits(end.X), + 4: math.Float32bits(end.Y), + } +} + +func Gap(start, end f32.Point) Command { + return Command{ + 0: uint32(OpGap), + 1: math.Float32bits(start.X), + 2: math.Float32bits(start.Y), + 3: math.Float32bits(end.X), + 4: math.Float32bits(end.Y), + } +} + +func Cubic(start, ctrl0, ctrl1, end f32.Point) Command { + return Command{ + 0: uint32(OpCubic), + 1: math.Float32bits(start.X), + 2: math.Float32bits(start.Y), + 3: math.Float32bits(ctrl0.X), + 4: math.Float32bits(ctrl0.Y), + 5: math.Float32bits(ctrl1.X), + 6: math.Float32bits(ctrl1.Y), + 7: math.Float32bits(end.X), + 8: math.Float32bits(end.Y), + } +} + +func Quad(start, ctrl, end f32.Point) Command { + return Command{ + 0: uint32(OpQuad), + 1: math.Float32bits(start.X), + 2: math.Float32bits(start.Y), + 3: math.Float32bits(ctrl.X), + 4: math.Float32bits(ctrl.Y), + 5: math.Float32bits(end.X), + 6: math.Float32bits(end.Y), + } +} + +func Transform(m f32.Affine2D) Command { + sx, hx, ox, hy, sy, oy := m.Elems() + return Command{ + 0: uint32(OpTransform), + 1: math.Float32bits(sx), + 2: math.Float32bits(hy), + 3: math.Float32bits(hx), + 4: math.Float32bits(sy), + 5: math.Float32bits(ox), + 6: math.Float32bits(oy), + } +} + +func SetLineWidth(width float32) Command { + return Command{ + 0: uint32(OpLineWidth), + 1: math.Float32bits(width), + } +} + +func BeginClip(bbox f32.Rectangle) Command { + return Command{ + 0: uint32(OpBeginClip), + 1: math.Float32bits(bbox.Min.X), + 2: math.Float32bits(bbox.Min.Y), + 3: math.Float32bits(bbox.Max.X), + 4: math.Float32bits(bbox.Max.Y), + } +} + +func EndClip(bbox f32.Rectangle) Command { + return Command{ + 0: uint32(OpEndClip), + 1: math.Float32bits(bbox.Min.X), + 2: math.Float32bits(bbox.Min.Y), + 3: math.Float32bits(bbox.Max.X), + 4: math.Float32bits(bbox.Max.Y), + } +} + +func FillColor(col color.RGBA) Command { + return Command{ + 0: uint32(OpFillColor), + 1: uint32(col.R)<<24 | uint32(col.G)<<16 | uint32(col.B)<<8 | uint32(col.A), + } +} + +func FillImage(index int, offset image.Point) Command { + x := int16(offset.X) + y := int16(offset.Y) + return Command{ + 0: uint32(OpFillImage), + 1: uint32(index), + 2: uint32(uint16(x)) | uint32(uint16(y))<<16, + } +} + +func SetFillMode(mode FillMode) Command { + return Command{ + 0: uint32(OpSetFillMode), + 1: uint32(mode), + } +} + +func DecodeLine(cmd Command) (from, to f32.Point) { + if cmd[0] != uint32(OpLine) { + panic("invalid command") + } + from = f32.Pt(math.Float32frombits(cmd[1]), math.Float32frombits(cmd[2])) + to = f32.Pt(math.Float32frombits(cmd[3]), math.Float32frombits(cmd[4])) + return +} + +func DecodeGap(cmd Command) (from, to f32.Point) { + if cmd[0] != uint32(OpGap) { + panic("invalid command") + } + from = f32.Pt(math.Float32frombits(cmd[1]), math.Float32frombits(cmd[2])) + to = f32.Pt(math.Float32frombits(cmd[3]), math.Float32frombits(cmd[4])) + return +} + +func DecodeQuad(cmd Command) (from, ctrl, to f32.Point) { + if cmd[0] != uint32(OpQuad) { + panic("invalid command") + } + from = f32.Pt(math.Float32frombits(cmd[1]), math.Float32frombits(cmd[2])) + ctrl = f32.Pt(math.Float32frombits(cmd[3]), math.Float32frombits(cmd[4])) + to = f32.Pt(math.Float32frombits(cmd[5]), math.Float32frombits(cmd[6])) + return +} + +func DecodeCubic(cmd Command) (from, ctrl0, ctrl1, to f32.Point) { + if cmd[0] != uint32(OpCubic) { + panic("invalid command") + } + from = f32.Pt(math.Float32frombits(cmd[1]), math.Float32frombits(cmd[2])) + ctrl0 = f32.Pt(math.Float32frombits(cmd[3]), math.Float32frombits(cmd[4])) + ctrl1 = f32.Pt(math.Float32frombits(cmd[5]), math.Float32frombits(cmd[6])) + to = f32.Pt(math.Float32frombits(cmd[7]), math.Float32frombits(cmd[8])) + return +} diff --git a/vendor/gioui.org/internal/stroke/stroke.go b/vendor/gioui.org/internal/stroke/stroke.go new file mode 100644 index 0000000..47db6ca --- /dev/null +++ b/vendor/gioui.org/internal/stroke/stroke.go @@ -0,0 +1,742 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +// Most of the algorithms to compute strokes and their offsets have been +// extracted, adapted from (and used as a reference implementation): +// - github.com/tdewolff/canvas (Licensed under MIT) +// +// These algorithms have been implemented from: +// Fast, precise flattening of cubic Bézier path and offset curves +// Thomas F. Hain, et al. +// +// An electronic version is available at: +// https://seant23.files.wordpress.com/2010/11/fastpreciseflatteningofbeziercurve.pdf +// +// Possible improvements (in term of speed and/or accuracy) on these +// algorithms are: +// +// - Polar Stroking: New Theory and Methods for Stroking Paths, +// M. Kilgard +// https://arxiv.org/pdf/2007.00308.pdf +// +// - https://raphlinus.github.io/graphics/curves/2019/12/23/flatten-quadbez.html +// R. Levien + +// Package stroke implements conversion of strokes to filled outlines. It is used as a +// fallback for stroke configurations not natively supported by the renderer. +package stroke + +import ( + "encoding/binary" + "math" + + "gioui.org/f32" + "gioui.org/internal/ops" + "gioui.org/internal/scene" +) + +// The following are copies of types from op/clip to avoid a circular import of +// that package. +// TODO: when the old renderer is gone, this package can be merged with +// op/clip, eliminating the duplicate types. +type StrokeStyle struct { + Width float32 +} + +// strokeTolerance is used to reconcile rounding errors arising +// when splitting quads into smaller and smaller segments to approximate +// them into straight lines, and when joining back segments. +// +// The magic value of 0.01 was found by striking a compromise between +// aesthetic looking (curves did look like curves, even after linearization) +// and speed. +const strokeTolerance = 0.01 + +type QuadSegment struct { + From, Ctrl, To f32.Point +} + +type StrokeQuad struct { + Contour uint32 + Quad QuadSegment +} + +type strokeState struct { + p0, p1 f32.Point // p0 is the start point, p1 the end point. + n0, n1 f32.Point // n0 is the normal vector at the start point, n1 at the end point. + r0, r1 float32 // r0 is the curvature at the start point, r1 at the end point. + ctl f32.Point // ctl is the control point of the quadratic Bézier segment. +} + +type StrokeQuads []StrokeQuad + +func (qs *StrokeQuads) setContour(n uint32) { + for i := range *qs { + (*qs)[i].Contour = n + } +} + +func (qs *StrokeQuads) pen() f32.Point { + return (*qs)[len(*qs)-1].Quad.To +} + +func (qs *StrokeQuads) lineTo(pt f32.Point) { + end := qs.pen() + *qs = append(*qs, StrokeQuad{ + Quad: QuadSegment{ + From: end, + Ctrl: end.Add(pt).Mul(0.5), + To: pt, + }, + }) +} + +func (qs *StrokeQuads) arc(f1, f2 f32.Point, angle float32) { + const segments = 16 + pen := qs.pen() + m := ArcTransform(pen, f1.Add(pen), f2.Add(pen), angle, segments) + for i := 0; i < segments; i++ { + p0 := qs.pen() + p1 := m.Transform(p0) + p2 := m.Transform(p1) + ctl := p1.Mul(2).Sub(p0.Add(p2).Mul(.5)) + *qs = append(*qs, StrokeQuad{ + Quad: QuadSegment{ + From: p0, Ctrl: ctl, To: p2, + }, + }) + } +} + +// split splits a slice of quads into slices of quads grouped +// by contours (ie: splitted at move-to boundaries). +func (qs StrokeQuads) split() []StrokeQuads { + if len(qs) == 0 { + return nil + } + + var ( + c uint32 + o []StrokeQuads + i = len(o) + ) + for _, q := range qs { + if q.Contour != c { + c = q.Contour + i = len(o) + o = append(o, StrokeQuads{}) + } + o[i] = append(o[i], q) + } + + return o +} + +func (qs StrokeQuads) stroke(stroke StrokeStyle) StrokeQuads { + var ( + o StrokeQuads + hw = 0.5 * stroke.Width + ) + + for _, ps := range qs.split() { + rhs, lhs := ps.offset(hw, stroke) + switch lhs { + case nil: + o = o.append(rhs) + default: + // Closed path. + // Inner path should go opposite direction to cancel outer path. + switch { + case ps.ccw(): + lhs = lhs.reverse() + o = o.append(rhs) + o = o.append(lhs) + default: + rhs = rhs.reverse() + o = o.append(lhs) + o = o.append(rhs) + } + } + } + + return o +} + +// offset returns the right-hand and left-hand sides of the path, offset by +// the half-width hw. +// The stroke handles how segments are joined and ends are capped. +func (qs StrokeQuads) offset(hw float32, stroke StrokeStyle) (rhs, lhs StrokeQuads) { + var ( + states []strokeState + beg = qs[0].Quad.From + end = qs[len(qs)-1].Quad.To + closed = beg == end + ) + for i := range qs { + q := qs[i].Quad + + var ( + n0 = strokePathNorm(q.From, q.Ctrl, q.To, 0, hw) + n1 = strokePathNorm(q.From, q.Ctrl, q.To, 1, hw) + r0 = strokePathCurv(q.From, q.Ctrl, q.To, 0) + r1 = strokePathCurv(q.From, q.Ctrl, q.To, 1) + ) + states = append(states, strokeState{ + p0: q.From, + p1: q.To, + n0: n0, + n1: n1, + r0: r0, + r1: r1, + ctl: q.Ctrl, + }) + } + + for i, state := range states { + rhs = rhs.append(strokeQuadBezier(state, +hw, strokeTolerance)) + lhs = lhs.append(strokeQuadBezier(state, -hw, strokeTolerance)) + + // join the current and next segments + if hasNext := i+1 < len(states); hasNext || closed { + var next strokeState + switch { + case hasNext: + next = states[i+1] + case closed: + next = states[0] + } + if state.n1 != next.n0 { + strokePathJoin(stroke, &rhs, &lhs, hw, state.p1, state.n1, next.n0, state.r1, next.r0) + } + } + } + + if closed { + rhs.close() + lhs.close() + return rhs, lhs + } + + qbeg := &states[0] + qend := &states[len(states)-1] + + // Default to counter-clockwise direction. + lhs = lhs.reverse() + strokePathCap(stroke, &rhs, hw, qend.p1, qend.n1) + + rhs = rhs.append(lhs) + strokePathCap(stroke, &rhs, hw, qbeg.p0, qbeg.n0.Mul(-1)) + + rhs.close() + + return rhs, nil +} + +func (qs *StrokeQuads) close() { + p0 := (*qs)[len(*qs)-1].Quad.To + p1 := (*qs)[0].Quad.From + + if p1 == p0 { + return + } + + *qs = append(*qs, StrokeQuad{ + Quad: QuadSegment{ + From: p0, + Ctrl: p0.Add(p1).Mul(0.5), + To: p1, + }, + }) +} + +// ccw returns whether the path is counter-clockwise. +func (qs StrokeQuads) ccw() bool { + // Use the Shoelace formula: + // https://en.wikipedia.org/wiki/Shoelace_formula + var area float32 + for _, ps := range qs.split() { + for i := 1; i < len(ps); i++ { + pi := ps[i].Quad.To + pj := ps[i-1].Quad.To + area += (pi.X - pj.X) * (pi.Y + pj.Y) + } + } + return area <= 0.0 +} + +func (qs StrokeQuads) reverse() StrokeQuads { + if len(qs) == 0 { + return nil + } + + ps := make(StrokeQuads, 0, len(qs)) + for i := range qs { + q := qs[len(qs)-1-i] + q.Quad.To, q.Quad.From = q.Quad.From, q.Quad.To + ps = append(ps, q) + } + + return ps +} + +func (qs StrokeQuads) append(ps StrokeQuads) StrokeQuads { + switch { + case len(ps) == 0: + return qs + case len(qs) == 0: + return ps + } + + // Consolidate quads and smooth out rounding errors. + // We need to also check for the strokeTolerance to correctly handle + // join/cap points or on-purpose disjoint quads. + p0 := qs[len(qs)-1].Quad.To + p1 := ps[0].Quad.From + if p0 != p1 && lenPt(p0.Sub(p1)) < strokeTolerance { + qs = append(qs, StrokeQuad{ + Quad: QuadSegment{ + From: p0, + Ctrl: p0.Add(p1).Mul(0.5), + To: p1, + }, + }) + } + return append(qs, ps...) +} + +func (q QuadSegment) Transform(t f32.Affine2D) QuadSegment { + q.From = t.Transform(q.From) + q.Ctrl = t.Transform(q.Ctrl) + q.To = t.Transform(q.To) + return q +} + +// strokePathNorm returns the normal vector at t. +func strokePathNorm(p0, p1, p2 f32.Point, t, d float32) f32.Point { + switch t { + case 0: + n := p1.Sub(p0) + if n.X == 0 && n.Y == 0 { + return f32.Point{} + } + n = rot90CW(n) + return normPt(n, d) + case 1: + n := p2.Sub(p1) + if n.X == 0 && n.Y == 0 { + return f32.Point{} + } + n = rot90CW(n) + return normPt(n, d) + } + panic("impossible") +} + +func rot90CW(p f32.Point) f32.Point { return f32.Pt(+p.Y, -p.X) } +func rot90CCW(p f32.Point) f32.Point { return f32.Pt(-p.Y, +p.X) } + +// cosPt returns the cosine of the opening angle between p and q. +func cosPt(p, q f32.Point) float32 { + np := math.Hypot(float64(p.X), float64(p.Y)) + nq := math.Hypot(float64(q.X), float64(q.Y)) + return dotPt(p, q) / float32(np*nq) +} + +func normPt(p f32.Point, l float32) f32.Point { + d := math.Hypot(float64(p.X), float64(p.Y)) + l64 := float64(l) + if math.Abs(d-l64) < 1e-10 { + return f32.Point{} + } + n := float32(l64 / d) + return f32.Point{X: p.X * n, Y: p.Y * n} +} + +func lenPt(p f32.Point) float32 { + return float32(math.Hypot(float64(p.X), float64(p.Y))) +} + +func dotPt(p, q f32.Point) float32 { + return p.X*q.X + p.Y*q.Y +} + +func perpDot(p, q f32.Point) float32 { + return p.X*q.Y - p.Y*q.X +} + +// strokePathCurv returns the curvature at t, along the quadratic Bézier +// curve defined by the triplet (beg, ctl, end). +func strokePathCurv(beg, ctl, end f32.Point, t float32) float32 { + var ( + d1p = quadBezierD1(beg, ctl, end, t) + d2p = quadBezierD2(beg, ctl, end, t) + + // Negative when bending right, ie: the curve is CW at this point. + a = float64(perpDot(d1p, d2p)) + ) + + // We check early that the segment isn't too line-like and + // save a costly call to math.Pow that will be discarded by dividing + // with a too small 'a'. + if math.Abs(a) < 1e-10 { + return float32(math.NaN()) + } + return float32(math.Pow(float64(d1p.X*d1p.X+d1p.Y*d1p.Y), 1.5) / a) +} + +// quadBezierSample returns the point on the Bézier curve at t. +// B(t) = (1-t)^2 P0 + 2(1-t)t P1 + t^2 P2 +func quadBezierSample(p0, p1, p2 f32.Point, t float32) f32.Point { + t1 := 1 - t + c0 := t1 * t1 + c1 := 2 * t1 * t + c2 := t * t + + o := p0.Mul(c0) + o = o.Add(p1.Mul(c1)) + o = o.Add(p2.Mul(c2)) + return o +} + +// quadBezierD1 returns the first derivative of the Bézier curve with respect to t. +// B'(t) = 2(1-t)(P1 - P0) + 2t(P2 - P1) +func quadBezierD1(p0, p1, p2 f32.Point, t float32) f32.Point { + p10 := p1.Sub(p0).Mul(2 * (1 - t)) + p21 := p2.Sub(p1).Mul(2 * t) + + return p10.Add(p21) +} + +// quadBezierD2 returns the second derivative of the Bézier curve with respect to t: +// B''(t) = 2(P2 - 2P1 + P0) +func quadBezierD2(p0, p1, p2 f32.Point, t float32) f32.Point { + p := p2.Sub(p1.Mul(2)).Add(p0) + return p.Mul(2) +} + +func strokeQuadBezier(state strokeState, d, flatness float32) StrokeQuads { + // Gio strokes are only quadratic Bézier curves, w/o any inflection point. + // So we just have to flatten them. + var qs StrokeQuads + return flattenQuadBezier(qs, state.p0, state.ctl, state.p1, d, flatness) +} + +// flattenQuadBezier splits a Bézier quadratic curve into linear sub-segments, +// themselves also encoded as Bézier (degenerate, flat) quadratic curves. +func flattenQuadBezier(qs StrokeQuads, p0, p1, p2 f32.Point, d, flatness float32) StrokeQuads { + var ( + t float32 + flat64 = float64(flatness) + ) + for t < 1 { + s2 := float64((p2.X-p0.X)*(p1.Y-p0.Y) - (p2.Y-p0.Y)*(p1.X-p0.X)) + den := math.Hypot(float64(p1.X-p0.X), float64(p1.Y-p0.Y)) + if s2*den == 0.0 { + break + } + + s2 /= den + t = 2.0 * float32(math.Sqrt(flat64/3.0/math.Abs(s2))) + if t >= 1.0 { + break + } + var q0, q1, q2 f32.Point + q0, q1, q2, p0, p1, p2 = quadBezierSplit(p0, p1, p2, t) + qs.addLine(q0, q1, q2, 0, d) + } + qs.addLine(p0, p1, p2, 1, d) + return qs +} + +func (qs *StrokeQuads) addLine(p0, ctrl, p1 f32.Point, t, d float32) { + + switch i := len(*qs); i { + case 0: + p0 = p0.Add(strokePathNorm(p0, ctrl, p1, 0, d)) + default: + // Address possible rounding errors and use previous point. + p0 = (*qs)[i-1].Quad.To + } + + p1 = p1.Add(strokePathNorm(p0, ctrl, p1, 1, d)) + + *qs = append(*qs, + StrokeQuad{ + Quad: QuadSegment{ + From: p0, + Ctrl: p0.Add(p1).Mul(0.5), + To: p1, + }, + }, + ) +} + +// quadInterp returns the interpolated point at t. +func quadInterp(p, q f32.Point, t float32) f32.Point { + return f32.Pt( + (1-t)*p.X+t*q.X, + (1-t)*p.Y+t*q.Y, + ) +} + +// quadBezierSplit returns the pair of triplets (from,ctrl,to) Bézier curve, +// split before (resp. after) the provided parametric t value. +func quadBezierSplit(p0, p1, p2 f32.Point, t float32) (f32.Point, f32.Point, f32.Point, f32.Point, f32.Point, f32.Point) { + + var ( + b0 = p0 + b1 = quadInterp(p0, p1, t) + b2 = quadBezierSample(p0, p1, p2, t) + + a0 = b2 + a1 = quadInterp(p1, p2, t) + a2 = p2 + ) + + return b0, b1, b2, a0, a1, a2 +} + +// strokePathJoin joins the two paths rhs and lhs, according to the provided +// stroke operation. +func strokePathJoin(stroke StrokeStyle, rhs, lhs *StrokeQuads, hw float32, pivot, n0, n1 f32.Point, r0, r1 float32) { + strokePathRoundJoin(rhs, lhs, hw, pivot, n0, n1, r0, r1) +} + +func strokePathRoundJoin(rhs, lhs *StrokeQuads, hw float32, pivot, n0, n1 f32.Point, r0, r1 float32) { + rp := pivot.Add(n1) + lp := pivot.Sub(n1) + cw := dotPt(rot90CW(n0), n1) >= 0.0 + switch { + case cw: + // Path bends to the right, ie. CW (or 180 degree turn). + c := pivot.Sub(lhs.pen()) + angle := -math.Acos(float64(cosPt(n0, n1))) + lhs.arc(c, c, float32(angle)) + lhs.lineTo(lp) // Add a line to accommodate for rounding errors. + rhs.lineTo(rp) + default: + // Path bends to the left, ie. CCW. + angle := math.Acos(float64(cosPt(n0, n1))) + c := pivot.Sub(rhs.pen()) + rhs.arc(c, c, float32(angle)) + rhs.lineTo(rp) // Add a line to accommodate for rounding errors. + lhs.lineTo(lp) + } +} + +// strokePathCap caps the provided path qs, according to the provided stroke operation. +func strokePathCap(stroke StrokeStyle, qs *StrokeQuads, hw float32, pivot, n0 f32.Point) { + strokePathRoundCap(qs, hw, pivot, n0) +} + +// strokePathRoundCap caps the start or end of a path with a round cap. +func strokePathRoundCap(qs *StrokeQuads, hw float32, pivot, n0 f32.Point) { + c := pivot.Sub(qs.pen()) + qs.arc(c, c, math.Pi) +} + +// ArcTransform computes a transformation that can be used for generating quadratic bézier +// curve approximations for an arc. +// +// The math is extracted from the following paper: +// "Drawing an elliptical arc using polylines, quadratic or +// cubic Bezier curves", L. Maisonobe +// An electronic version may be found at: +// http://spaceroots.org/documents/ellipse/elliptical-arc.pdf +func ArcTransform(p, f1, f2 f32.Point, angle float32, segments int) f32.Affine2D { + var rx, ry, alpha float64 + if f1 == f2 { + // degenerate case of a circle. + rx = dist(f1, p) + ry = rx + } else { + // semi-major axis: 2a = |PF1| + |PF2| + a := 0.5 * (dist(f1, p) + dist(f2, p)) + // semi-minor axis: c^2 = a^2 - b^2 (c: focal distance) + c := dist(f1, f2) * 0.5 + b := math.Sqrt(a*a - c*c) + switch { + case a > b: + rx = a + ry = b + default: + rx = b + ry = a + } + if f1.X == f2.X { + // special case of a "vertical" ellipse. + alpha = math.Pi / 2 + if f1.Y < f2.Y { + alpha = -alpha + } + } else { + x := float64(f1.X-f2.X) * 0.5 + if x < 0 { + x = -x + } + alpha = math.Acos(x / c) + } + } + + var ( + θ = angle / float32(segments) + ref f32.Affine2D // transform from absolute frame to ellipse-based one + rot f32.Affine2D // rotation matrix for each segment + inv f32.Affine2D // transform from ellipse-based frame to absolute one + ) + center := f32.Point{ + X: 0.5 * (f1.X + f2.X), + Y: 0.5 * (f1.Y + f2.Y), + } + ref = ref.Offset(f32.Point{}.Sub(center)) + ref = ref.Rotate(f32.Point{}, float32(-alpha)) + ref = ref.Scale(f32.Point{}, f32.Point{ + X: float32(1 / rx), + Y: float32(1 / ry), + }) + inv = ref.Invert() + rot = rot.Rotate(f32.Point{}, 0.5*θ) + + // Instead of invoking math.Sincos for every segment, compute a rotation + // matrix once and apply for each segment. + // Before applying the rotation matrix rot, transform the coordinates + // to a frame centered to the ellipse (and warped into a unit circle), then rotate. + // Finally, transform back into the original frame. + return inv.Mul(rot).Mul(ref) +} + +func dist(p1, p2 f32.Point) float64 { + var ( + x1 = float64(p1.X) + y1 = float64(p1.Y) + x2 = float64(p2.X) + y2 = float64(p2.Y) + dx = x2 - x1 + dy = y2 - y1 + ) + return math.Hypot(dx, dy) +} + +func StrokePathCommands(style StrokeStyle, scene []byte) StrokeQuads { + quads := decodeToStrokeQuads(scene) + return quads.stroke(style) +} + +// decodeToStrokeQuads decodes scene commands to quads ready to stroke. +func decodeToStrokeQuads(pathData []byte) StrokeQuads { + quads := make(StrokeQuads, 0, 2*len(pathData)/(scene.CommandSize+4)) + for len(pathData) >= scene.CommandSize+4 { + contour := binary.LittleEndian.Uint32(pathData) + cmd := ops.DecodeCommand(pathData[4:]) + switch cmd.Op() { + case scene.OpLine: + var q QuadSegment + q.From, q.To = scene.DecodeLine(cmd) + q.Ctrl = q.From.Add(q.To).Mul(.5) + quad := StrokeQuad{ + Contour: contour, + Quad: q, + } + quads = append(quads, quad) + case scene.OpGap: + // Ignore gaps for strokes. + case scene.OpQuad: + var q QuadSegment + q.From, q.Ctrl, q.To = scene.DecodeQuad(cmd) + quad := StrokeQuad{ + Contour: contour, + Quad: q, + } + quads = append(quads, quad) + case scene.OpCubic: + for _, q := range SplitCubic(scene.DecodeCubic(cmd)) { + quad := StrokeQuad{ + Contour: contour, + Quad: q, + } + quads = append(quads, quad) + } + default: + panic("unsupported scene command") + } + pathData = pathData[scene.CommandSize+4:] + } + return quads +} + +func SplitCubic(from, ctrl0, ctrl1, to f32.Point) []QuadSegment { + quads := make([]QuadSegment, 0, 10) + // Set the maximum distance proportionally to the longest side + // of the bounding rectangle. + hull := f32.Rectangle{ + Min: from, + Max: ctrl0, + }.Canon().Union(f32.Rectangle{ + Min: ctrl1, + Max: to, + }.Canon()) + l := hull.Dx() + if h := hull.Dy(); h > l { + l = h + } + approxCubeTo(&quads, 0, l*0.001, from, ctrl0, ctrl1, to) + return quads +} + +// approxCubeTo approximates a cubic Bézier by a series of quadratic +// curves. +func approxCubeTo(quads *[]QuadSegment, splits int, maxDist float32, from, ctrl0, ctrl1, to f32.Point) int { + // The idea is from + // https://caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html + // where a quadratic approximates a cubic by eliminating its t³ term + // from its polynomial expression anchored at the starting point: + // + // P(t) = pen + 3t(ctrl0 - pen) + 3t²(ctrl1 - 2ctrl0 + pen) + t³(to - 3ctrl1 + 3ctrl0 - pen) + // + // The control point for the new quadratic Q1 that shares starting point, pen, with P is + // + // C1 = (3ctrl0 - pen)/2 + // + // The reverse cubic anchored at the end point has the polynomial + // + // P'(t) = to + 3t(ctrl1 - to) + 3t²(ctrl0 - 2ctrl1 + to) + t³(pen - 3ctrl0 + 3ctrl1 - to) + // + // The corresponding quadratic Q2 that shares the end point, to, with P has control + // point + // + // C2 = (3ctrl1 - to)/2 + // + // The combined quadratic Bézier, Q, shares both start and end points with its cubic + // and use the midpoint between the two curves Q1 and Q2 as control point: + // + // C = (3ctrl0 - pen + 3ctrl1 - to)/4 + c := ctrl0.Mul(3).Sub(from).Add(ctrl1.Mul(3)).Sub(to).Mul(1.0 / 4.0) + const maxSplits = 32 + if splits >= maxSplits { + *quads = append(*quads, QuadSegment{From: from, Ctrl: c, To: to}) + return splits + } + // The maximum distance between the cubic P and its approximation Q given t + // can be shown to be + // + // d = sqrt(3)/36*|to - 3ctrl1 + 3ctrl0 - pen| + // + // To save a square root, compare d² with the squared tolerance. + v := to.Sub(ctrl1.Mul(3)).Add(ctrl0.Mul(3)).Sub(from) + d2 := (v.X*v.X + v.Y*v.Y) * 3 / (36 * 36) + if d2 <= maxDist*maxDist { + *quads = append(*quads, QuadSegment{From: from, Ctrl: c, To: to}) + return splits + } + // De Casteljau split the curve and approximate the halves. + t := float32(0.5) + c0 := from.Add(ctrl0.Sub(from).Mul(t)) + c1 := ctrl0.Add(ctrl1.Sub(ctrl0).Mul(t)) + c2 := ctrl1.Add(to.Sub(ctrl1).Mul(t)) + c01 := c0.Add(c1.Sub(c0).Mul(t)) + c12 := c1.Add(c2.Sub(c1).Mul(t)) + c0112 := c01.Add(c12.Sub(c01).Mul(t)) + splits++ + splits = approxCubeTo(quads, splits, maxDist, from, c0, c01, c0112) + splits = approxCubeTo(quads, splits, maxDist, c0112, c12, c2, to) + return splits +} diff --git a/vendor/gioui.org/internal/unsafe/unsafe.go b/vendor/gioui.org/internal/unsafe/unsafe.go deleted file mode 100644 index 5353144..0000000 --- a/vendor/gioui.org/internal/unsafe/unsafe.go +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package unsafe - -import ( - "reflect" - "unsafe" -) - -// BytesView returns a byte slice view of a slice. -func BytesView(s interface{}) []byte { - v := reflect.ValueOf(s) - first := v.Index(0) - sz := int(first.Type().Size()) - return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ - Data: uintptr(unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(first.UnsafeAddr())))), - Len: v.Len() * sz, - Cap: v.Cap() * sz, - })) -} - -// SliceOf returns a slice from a (native) pointer. -func SliceOf(s uintptr) []byte { - if s == 0 { - return nil - } - sh := reflect.SliceHeader{ - Data: s, - Len: 1 << 30, - Cap: 1 << 30, - } - return *(*[]byte)(unsafe.Pointer(&sh)) -} - -// GoString convert a NUL-terminated C string -// to a Go string. -func GoString(s []byte) string { - i := 0 - for { - if s[i] == 0 { - break - } - i++ - } - return string(s[:i]) -} diff --git a/vendor/gioui.org/internal/vk/vulkan.go b/vendor/gioui.org/internal/vk/vulkan.go new file mode 100644 index 0000000..f210d40 --- /dev/null +++ b/vendor/gioui.org/internal/vk/vulkan.go @@ -0,0 +1,2081 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build linux || freebsd +// +build linux freebsd + +package vk + +/* +#cgo linux freebsd LDFLAGS: -ldl +#cgo freebsd CFLAGS: -I/usr/local/include +#cgo CFLAGS: -Werror -Werror=return-type + +#define VK_NO_PROTOTYPES 1 +#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object; +#include <vulkan/vulkan.h> +#define __USE_GNU +#include <dlfcn.h> +#include <stdlib.h> + +static VkResult vkCreateInstance(PFN_vkCreateInstance f, VkInstanceCreateInfo pCreateInfo, const VkAllocationCallbacks *pAllocator, VkInstance *pInstance) { + return f(&pCreateInfo, pAllocator, pInstance); +} + +static void vkDestroyInstance(PFN_vkDestroyInstance f, VkInstance instance, const VkAllocationCallbacks *pAllocator) { + f(instance, pAllocator); +} + +static VkResult vkEnumeratePhysicalDevices(PFN_vkEnumeratePhysicalDevices f, VkInstance instance, uint32_t *pPhysicalDeviceCount, VkPhysicalDevice *pPhysicalDevices) { + return f(instance, pPhysicalDeviceCount, pPhysicalDevices); +} + +static void vkGetPhysicalDeviceQueueFamilyProperties(PFN_vkGetPhysicalDeviceQueueFamilyProperties f, VkPhysicalDevice physicalDevice, uint32_t *pQueueFamilyPropertyCount, VkQueueFamilyProperties *pQueueFamilyProperties) { + f(physicalDevice, pQueueFamilyPropertyCount, pQueueFamilyProperties); +} + +static void vkGetPhysicalDeviceFormatProperties(PFN_vkGetPhysicalDeviceFormatProperties f, VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties *pFormatProperties) { + f(physicalDevice, format, pFormatProperties); +} + +static VkResult vkCreateDevice(PFN_vkCreateDevice f, VkPhysicalDevice physicalDevice, VkDeviceCreateInfo pCreateInfo, VkDeviceQueueCreateInfo qinf, const VkAllocationCallbacks *pAllocator, VkDevice *pDevice) { + pCreateInfo.pQueueCreateInfos = &qinf; + return f(physicalDevice, &pCreateInfo, pAllocator, pDevice); +} + +static void vkDestroyDevice(PFN_vkDestroyDevice f, VkDevice device, const VkAllocationCallbacks *pAllocator) { + f(device, pAllocator); +} + +static void vkGetDeviceQueue(PFN_vkGetDeviceQueue f, VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue *pQueue) { + f(device, queueFamilyIndex, queueIndex, pQueue); +} + +static VkResult vkCreateImageView(PFN_vkCreateImageView f, VkDevice device, const VkImageViewCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkImageView *pView) { + return f(device, pCreateInfo, pAllocator, pView); +} + +static void vkDestroyImageView(PFN_vkDestroyImageView f, VkDevice device, VkImageView imageView, const VkAllocationCallbacks *pAllocator) { + f(device, imageView, pAllocator); +} + +static VkResult vkCreateFramebuffer(PFN_vkCreateFramebuffer f, VkDevice device, VkFramebufferCreateInfo pCreateInfo, const VkAllocationCallbacks *pAllocator, VkFramebuffer *pFramebuffer) { + return f(device, &pCreateInfo, pAllocator, pFramebuffer); +} + +static void vkDestroyFramebuffer(PFN_vkDestroyFramebuffer f, VkDevice device, VkFramebuffer framebuffer, const VkAllocationCallbacks *pAllocator) { + f(device, framebuffer, pAllocator); +} + +static VkResult vkDeviceWaitIdle(PFN_vkDeviceWaitIdle f, VkDevice device) { + return f(device); +} + +static VkResult vkQueueWaitIdle(PFN_vkQueueWaitIdle f, VkQueue queue) { + return f(queue); +} + +static VkResult vkCreateSemaphore(PFN_vkCreateSemaphore f, VkDevice device, const VkSemaphoreCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSemaphore *pSemaphore) { + return f(device, pCreateInfo, pAllocator, pSemaphore); +} + +static void vkDestroySemaphore(PFN_vkDestroySemaphore f, VkDevice device, VkSemaphore semaphore, const VkAllocationCallbacks *pAllocator) { + f(device, semaphore, pAllocator); +} + +static VkResult vkCreateRenderPass(PFN_vkCreateRenderPass f, VkDevice device, VkRenderPassCreateInfo pCreateInfo, VkSubpassDescription subpassInf, const VkAllocationCallbacks *pAllocator, VkRenderPass *pRenderPass) { + pCreateInfo.pSubpasses = &subpassInf; + return f(device, &pCreateInfo, pAllocator, pRenderPass); +} + +static void vkDestroyRenderPass(PFN_vkDestroyRenderPass f, VkDevice device, VkRenderPass renderPass, const VkAllocationCallbacks *pAllocator) { + f(device, renderPass, pAllocator); +} + +static VkResult vkCreateCommandPool(PFN_vkCreateCommandPool f, VkDevice device, const VkCommandPoolCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkCommandPool *pCommandPool) { + return f(device, pCreateInfo, pAllocator, pCommandPool); +} + +static void vkDestroyCommandPool(PFN_vkDestroyCommandPool f, VkDevice device, VkCommandPool commandPool, const VkAllocationCallbacks *pAllocator) { + f(device, commandPool, pAllocator); +} + +static VkResult vkAllocateCommandBuffers(PFN_vkAllocateCommandBuffers f, VkDevice device, const VkCommandBufferAllocateInfo *pAllocateInfo, VkCommandBuffer *pCommandBuffers) { + return f(device, pAllocateInfo, pCommandBuffers); +} + +static void vkFreeCommandBuffers(PFN_vkFreeCommandBuffers f, VkDevice device, VkCommandPool commandPool, uint32_t commandBufferCount, const VkCommandBuffer *pCommandBuffers) { + f(device, commandPool, commandBufferCount, pCommandBuffers); +} + +static VkResult vkBeginCommandBuffer(PFN_vkBeginCommandBuffer f, VkCommandBuffer commandBuffer, VkCommandBufferBeginInfo pBeginInfo) { + return f(commandBuffer, &pBeginInfo); +} + +static VkResult vkEndCommandBuffer(PFN_vkEndCommandBuffer f, VkCommandBuffer commandBuffer) { + return f(commandBuffer); +} + +static VkResult vkQueueSubmit(PFN_vkQueueSubmit f, VkQueue queue, VkSubmitInfo pSubmits, VkFence fence) { + return f(queue, 1, &pSubmits, fence); +} + +static void vkCmdBeginRenderPass(PFN_vkCmdBeginRenderPass f, VkCommandBuffer commandBuffer, VkRenderPassBeginInfo pRenderPassBegin, VkSubpassContents contents) { + f(commandBuffer, &pRenderPassBegin, contents); +} + +static void vkCmdEndRenderPass(PFN_vkCmdEndRenderPass f, VkCommandBuffer commandBuffer) { + f(commandBuffer); +} + +static void vkCmdCopyBuffer(PFN_vkCmdCopyBuffer f, VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferCopy *pRegions) { + f(commandBuffer, srcBuffer, dstBuffer, regionCount, pRegions); +} + +static void vkCmdCopyBufferToImage(PFN_vkCmdCopyBufferToImage f, VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy *pRegions) { + f(commandBuffer, srcBuffer, dstImage, dstImageLayout, regionCount, pRegions); +} + +static void vkCmdPipelineBarrier(PFN_vkCmdPipelineBarrier f, VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags, uint32_t memoryBarrierCount, const VkMemoryBarrier *pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier *pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier *pImageMemoryBarriers) { + f(commandBuffer, srcStageMask, dstStageMask, dependencyFlags, memoryBarrierCount, pMemoryBarriers, bufferMemoryBarrierCount, pBufferMemoryBarriers, imageMemoryBarrierCount, pImageMemoryBarriers); +} + +static void vkCmdPushConstants(PFN_vkCmdPushConstants f, VkCommandBuffer commandBuffer, VkPipelineLayout layout, VkShaderStageFlags stageFlags, uint32_t offset, uint32_t size, const void *pValues) { + f(commandBuffer, layout, stageFlags, offset, size, pValues); +} + +static void vkCmdBindPipeline(PFN_vkCmdBindPipeline f, VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipeline pipeline) { + f(commandBuffer, pipelineBindPoint, pipeline); +} + +static void vkCmdBindVertexBuffers(PFN_vkCmdBindVertexBuffers f, VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer *pBuffers, const VkDeviceSize *pOffsets) { + f(commandBuffer, firstBinding, bindingCount, pBuffers, pOffsets); +} + +static void vkCmdSetViewport(PFN_vkCmdSetViewport f, VkCommandBuffer commandBuffer, uint32_t firstViewport, uint32_t viewportCount, const VkViewport *pViewports) { + f(commandBuffer, firstViewport, viewportCount, pViewports); +} + +static void vkCmdBindIndexBuffer(PFN_vkCmdBindIndexBuffer f, VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkIndexType indexType) { + f(commandBuffer, buffer, offset, indexType); +} + +static void vkCmdDraw(PFN_vkCmdDraw f, VkCommandBuffer commandBuffer, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) { + f(commandBuffer, vertexCount, instanceCount, firstVertex, firstInstance); +} + +static void vkCmdDrawIndexed(PFN_vkCmdDrawIndexed f, VkCommandBuffer commandBuffer, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance) { + f(commandBuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance); +} + +static void vkCmdBindDescriptorSets(PFN_vkCmdBindDescriptorSets f, VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t firstSet, uint32_t descriptorSetCount, const VkDescriptorSet *pDescriptorSets, uint32_t dynamicOffsetCount, const uint32_t *pDynamicOffsets) { + f(commandBuffer, pipelineBindPoint, layout, firstSet, descriptorSetCount, pDescriptorSets, dynamicOffsetCount, pDynamicOffsets); +} + +static void vkCmdCopyImageToBuffer(PFN_vkCmdCopyImageToBuffer f, VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy *pRegions) { + f(commandBuffer, srcImage, srcImageLayout, dstBuffer, regionCount, pRegions); +} + +static void vkCmdDispatch(PFN_vkCmdDispatch f, VkCommandBuffer commandBuffer, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) { + f(commandBuffer, groupCountX, groupCountY, groupCountZ); +} + +static VkResult vkCreateImage(PFN_vkCreateImage f, VkDevice device, const VkImageCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkImage *pImage) { + return f(device, pCreateInfo, pAllocator, pImage); +} + +static void vkDestroyImage(PFN_vkDestroyImage f, VkDevice device, VkImage image, const VkAllocationCallbacks *pAllocator) { + f(device, image, pAllocator); +} + +static void vkGetImageMemoryRequirements(PFN_vkGetImageMemoryRequirements f, VkDevice device, VkImage image, VkMemoryRequirements *pMemoryRequirements) { + f(device, image, pMemoryRequirements); +} + +static VkResult vkAllocateMemory(PFN_vkAllocateMemory f, VkDevice device, const VkMemoryAllocateInfo *pAllocateInfo, const VkAllocationCallbacks *pAllocator, VkDeviceMemory *pMemory) { + return f(device, pAllocateInfo, pAllocator, pMemory); +} + +static VkResult vkBindImageMemory(PFN_vkBindImageMemory f, VkDevice device, VkImage image, VkDeviceMemory memory, VkDeviceSize memoryOffset) { + return f(device, image, memory, memoryOffset); +} + +static void vkFreeMemory(PFN_vkFreeMemory f, VkDevice device, VkDeviceMemory memory, const VkAllocationCallbacks *pAllocator) { + f(device, memory, pAllocator); +} + +static void vkGetPhysicalDeviceMemoryProperties(PFN_vkGetPhysicalDeviceMemoryProperties f, VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties *pMemoryProperties) { + f(physicalDevice, pMemoryProperties); +} + +static VkResult vkCreateSampler(PFN_vkCreateSampler f,VkDevice device, const VkSamplerCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSampler *pSampler) { + return f(device, pCreateInfo, pAllocator, pSampler); +} + +static void vkDestroySampler(PFN_vkDestroySampler f, VkDevice device, VkSampler sampler, const VkAllocationCallbacks *pAllocator) { + f(device, sampler, pAllocator); +} + +static VkResult vkCreateBuffer(PFN_vkCreateBuffer f, VkDevice device, const VkBufferCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkBuffer *pBuffer) { + return f(device, pCreateInfo, pAllocator, pBuffer); +} + +static void vkDestroyBuffer(PFN_vkDestroyBuffer f, VkDevice device, VkBuffer buffer, const VkAllocationCallbacks *pAllocator) { + f(device, buffer, pAllocator); +} + +static void vkGetBufferMemoryRequirements(PFN_vkGetBufferMemoryRequirements f, VkDevice device, VkBuffer buffer, VkMemoryRequirements *pMemoryRequirements) { + f(device, buffer, pMemoryRequirements); +} + +static VkResult vkBindBufferMemory(PFN_vkBindBufferMemory f, VkDevice device, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset) { + return f(device, buffer, memory, memoryOffset); +} + +static VkResult vkCreateShaderModule(PFN_vkCreateShaderModule f, VkDevice device, VkShaderModuleCreateInfo pCreateInfo, const VkAllocationCallbacks *pAllocator, VkShaderModule *pShaderModule) { + return f(device, &pCreateInfo, pAllocator, pShaderModule); +} + +static void vkDestroyShaderModule(PFN_vkDestroyShaderModule f, VkDevice device, VkShaderModule shaderModule, const VkAllocationCallbacks *pAllocator) { + f(device, shaderModule, pAllocator); +} + +static VkResult vkCreateGraphicsPipelines(PFN_vkCreateGraphicsPipelines f, VkDevice device, VkPipelineCache pipelineCache, VkGraphicsPipelineCreateInfo pCreateInfo, VkPipelineDynamicStateCreateInfo dynInf, VkPipelineColorBlendStateCreateInfo blendInf, VkPipelineVertexInputStateCreateInfo vertexInf, VkPipelineViewportStateCreateInfo viewportInf, const VkAllocationCallbacks *pAllocator, VkPipeline *pPipelines) { + pCreateInfo.pDynamicState = &dynInf; + pCreateInfo.pViewportState = &viewportInf; + pCreateInfo.pColorBlendState = &blendInf; + pCreateInfo.pVertexInputState = &vertexInf; + return f(device, pipelineCache, 1, &pCreateInfo, pAllocator, pPipelines); +} + +static void vkDestroyPipeline(PFN_vkDestroyPipeline f, VkDevice device, VkPipeline pipeline, const VkAllocationCallbacks *pAllocator) { + f(device, pipeline, pAllocator); +} + +static VkResult vkCreatePipelineLayout(PFN_vkCreatePipelineLayout f, VkDevice device, VkPipelineLayoutCreateInfo pCreateInfo, const VkAllocationCallbacks *pAllocator, VkPipelineLayout *pPipelineLayout) { + return f(device, &pCreateInfo, pAllocator, pPipelineLayout); +} + +static void vkDestroyPipelineLayout(PFN_vkDestroyPipelineLayout f, VkDevice device, VkPipelineLayout pipelineLayout, const VkAllocationCallbacks *pAllocator) { + f(device, pipelineLayout, pAllocator); +} + +static VkResult vkCreateDescriptorSetLayout(PFN_vkCreateDescriptorSetLayout f, VkDevice device, VkDescriptorSetLayoutCreateInfo pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDescriptorSetLayout *pSetLayout) { + return f(device, &pCreateInfo, pAllocator, pSetLayout); +} + +static void vkDestroyDescriptorSetLayout(PFN_vkDestroyDescriptorSetLayout f, VkDevice device, VkDescriptorSetLayout descriptorSetLayout, const VkAllocationCallbacks *pAllocator) { + f(device, descriptorSetLayout, pAllocator); +} + +static VkResult vkMapMemory(PFN_vkMapMemory f, VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void **ppData) { + return f(device, memory, offset, size, flags, ppData); +} + +static void vkUnmapMemory(PFN_vkUnmapMemory f, VkDevice device, VkDeviceMemory memory) { + f(device, memory); +} + +static VkResult vkResetCommandBuffer(PFN_vkResetCommandBuffer f, VkCommandBuffer commandBuffer, VkCommandBufferResetFlags flags) { + return f(commandBuffer, flags); +} + +static VkResult vkCreateDescriptorPool(PFN_vkCreateDescriptorPool f, VkDevice device, VkDescriptorPoolCreateInfo pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDescriptorPool *pDescriptorPool) { + return f(device, &pCreateInfo, pAllocator, pDescriptorPool); +} + +static void vkDestroyDescriptorPool(PFN_vkDestroyDescriptorPool f, VkDevice device, VkDescriptorPool descriptorPool, const VkAllocationCallbacks *pAllocator) { + f(device, descriptorPool, pAllocator); +} + +static VkResult vkAllocateDescriptorSets(PFN_vkAllocateDescriptorSets f, VkDevice device, VkDescriptorSetAllocateInfo pAllocateInfo, VkDescriptorSet *pDescriptorSets) { + return f(device, &pAllocateInfo, pDescriptorSets); +} + +static VkResult vkFreeDescriptorSets(PFN_vkFreeDescriptorSets f, VkDevice device, VkDescriptorPool descriptorPool, uint32_t descriptorSetCount, const VkDescriptorSet *pDescriptorSets) { + return f(device, descriptorPool, descriptorSetCount, pDescriptorSets); +} + +static void vkUpdateDescriptorSets(PFN_vkUpdateDescriptorSets f, VkDevice device, VkWriteDescriptorSet pDescriptorWrite, uint32_t descriptorCopyCount, const VkCopyDescriptorSet *pDescriptorCopies) { + f(device, 1, &pDescriptorWrite, descriptorCopyCount, pDescriptorCopies); +} + +static VkResult vkResetDescriptorPool(PFN_vkResetDescriptorPool f, VkDevice device, VkDescriptorPool descriptorPool, VkDescriptorPoolResetFlags flags) { + return f(device, descriptorPool, flags); +} + +static void vkCmdCopyImage(PFN_vkCmdCopyImage f, VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy *pRegions) { + f(commandBuffer, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, pRegions); +} + +static VkResult vkCreateComputePipelines(PFN_vkCreateComputePipelines f, VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkComputePipelineCreateInfo *pCreateInfos, const VkAllocationCallbacks *pAllocator, VkPipeline *pPipelines) { + return f(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines); +} + +static VkResult vkCreateFence(PFN_vkCreateFence f, VkDevice device, const VkFenceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkFence *pFence) { + return f(device, pCreateInfo, pAllocator, pFence); +} + +static void vkDestroyFence(PFN_vkDestroyFence f, VkDevice device, VkFence fence, const VkAllocationCallbacks *pAllocator) { + f(device, fence, pAllocator); +} + +static VkResult vkWaitForFences(PFN_vkWaitForFences f, VkDevice device, uint32_t fenceCount, const VkFence *pFences, VkBool32 waitAll, uint64_t timeout) { + return f(device, fenceCount, pFences, waitAll, timeout); +} + +static VkResult vkResetFences(PFN_vkResetFences f, VkDevice device, uint32_t fenceCount, const VkFence *pFences) { + return f(device, fenceCount, pFences); +} + +static void vkGetPhysicalDeviceProperties(PFN_vkGetPhysicalDeviceProperties f, VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties *pProperties) { + f(physicalDevice, pProperties); +} + +static VkResult vkGetPhysicalDeviceSurfaceSupportKHR(PFN_vkGetPhysicalDeviceSurfaceSupportKHR f, VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, VkSurfaceKHR surface, VkBool32 *pSupported) { + return f(physicalDevice, queueFamilyIndex, surface, pSupported); +} + +static void vkDestroySurfaceKHR(PFN_vkDestroySurfaceKHR f, VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks *pAllocator) { + f(instance, surface, pAllocator); +} + +static VkResult vkGetPhysicalDeviceSurfaceFormatsKHR(PFN_vkGetPhysicalDeviceSurfaceFormatsKHR f, VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t *pSurfaceFormatCount, VkSurfaceFormatKHR *pSurfaceFormats) { + return f(physicalDevice, surface, pSurfaceFormatCount, pSurfaceFormats); +} + +static VkResult vkGetPhysicalDeviceSurfacePresentModesKHR(PFN_vkGetPhysicalDeviceSurfacePresentModesKHR f, VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t *pPresentModeCount, VkPresentModeKHR *pPresentModes) { + return f(physicalDevice, surface, pPresentModeCount, pPresentModes); +} + +static VkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR(PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR f, VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR *pSurfaceCapabilities) { + return f(physicalDevice, surface, pSurfaceCapabilities); +} + +static VkResult vkCreateSwapchainKHR(PFN_vkCreateSwapchainKHR f, VkDevice device, const VkSwapchainCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSwapchainKHR *pSwapchain) { + return f(device, pCreateInfo, pAllocator, pSwapchain); +} + +static void vkDestroySwapchainKHR(PFN_vkDestroySwapchainKHR f, VkDevice device, VkSwapchainKHR swapchain, const VkAllocationCallbacks *pAllocator) { + f(device, swapchain, pAllocator); +} + +static VkResult vkGetSwapchainImagesKHR(PFN_vkGetSwapchainImagesKHR f, VkDevice device, VkSwapchainKHR swapchain, uint32_t *pSwapchainImageCount, VkImage *pSwapchainImages) { + return f(device, swapchain, pSwapchainImageCount, pSwapchainImages); +} + +// indexAndResult holds both an integer and a result returned by value, to +// avoid Go heap allocation of the integer with Vulkan's return style. +struct intAndResult { + uint32_t uint; + VkResult res; +}; + +static struct intAndResult vkAcquireNextImageKHR(PFN_vkAcquireNextImageKHR f, VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, VkFence fence) { + struct intAndResult res; + res.res = f(device, swapchain, timeout, semaphore, fence, &res.uint); + return res; +} + +static VkResult vkQueuePresentKHR(PFN_vkQueuePresentKHR f, VkQueue queue, const VkPresentInfoKHR pPresentInfo) { + return f(queue, &pPresentInfo); +} +*/ +import "C" +import ( + "errors" + "fmt" + "image" + "math" + "reflect" + "runtime" + "sync" + "unsafe" +) + +type ( + AttachmentLoadOp = C.VkAttachmentLoadOp + AccessFlags = C.VkAccessFlags + BlendFactor = C.VkBlendFactor + Buffer = C.VkBuffer + BufferImageCopy = C.VkBufferImageCopy + BufferMemoryBarrier = C.VkBufferMemoryBarrier + BufferUsageFlags = C.VkBufferUsageFlags + CommandPool = C.VkCommandPool + CommandBuffer = C.VkCommandBuffer + DependencyFlags = C.VkDependencyFlags + DescriptorPool = C.VkDescriptorPool + DescriptorPoolSize = C.VkDescriptorPoolSize + DescriptorSet = C.VkDescriptorSet + DescriptorSetLayout = C.VkDescriptorSetLayout + DescriptorType = C.VkDescriptorType + Device = C.VkDevice + DeviceMemory = C.VkDeviceMemory + DeviceSize = C.VkDeviceSize + Fence = C.VkFence + Queue = C.VkQueue + IndexType = C.VkIndexType + Image = C.VkImage + ImageCopy = C.VkImageCopy + ImageLayout = C.VkImageLayout + ImageMemoryBarrier = C.VkImageMemoryBarrier + ImageUsageFlags = C.VkImageUsageFlags + ImageView = C.VkImageView + Instance = C.VkInstance + Filter = C.VkFilter + Format = C.VkFormat + FormatFeatureFlags = C.VkFormatFeatureFlags + Framebuffer = C.VkFramebuffer + MemoryBarrier = C.VkMemoryBarrier + MemoryPropertyFlags = C.VkMemoryPropertyFlags + Pipeline = C.VkPipeline + PipelineBindPoint = C.VkPipelineBindPoint + PipelineLayout = C.VkPipelineLayout + PipelineStageFlags = C.VkPipelineStageFlags + PhysicalDevice = C.VkPhysicalDevice + PrimitiveTopology = C.VkPrimitiveTopology + PushConstantRange = C.VkPushConstantRange + QueueFamilyProperties = C.VkQueueFamilyProperties + QueueFlags = C.VkQueueFlags + RenderPass = C.VkRenderPass + Sampler = C.VkSampler + SamplerMipmapMode = C.VkSamplerMipmapMode + Semaphore = C.VkSemaphore + ShaderModule = C.VkShaderModule + ShaderStageFlags = C.VkShaderStageFlags + SubpassDependency = C.VkSubpassDependency + Viewport = C.VkViewport + WriteDescriptorSet = C.VkWriteDescriptorSet + + Surface = C.VkSurfaceKHR + SurfaceCapabilities = C.VkSurfaceCapabilitiesKHR + + Swapchain = C.VkSwapchainKHR +) + +type VertexInputBindingDescription struct { + Binding int + Stride int +} + +type VertexInputAttributeDescription struct { + Location int + Binding int + Format Format + Offset int +} + +type DescriptorSetLayoutBinding struct { + Binding int + DescriptorType DescriptorType + StageFlags ShaderStageFlags +} + +type Error C.VkResult + +const ( + FORMAT_R8G8B8A8_UNORM Format = C.VK_FORMAT_R8G8B8A8_UNORM + FORMAT_B8G8R8A8_SRGB Format = C.VK_FORMAT_B8G8R8A8_SRGB + FORMAT_R8G8B8A8_SRGB Format = C.VK_FORMAT_R8G8B8A8_SRGB + FORMAT_R16_SFLOAT Format = C.VK_FORMAT_R16_SFLOAT + FORMAT_R32_SFLOAT Format = C.VK_FORMAT_R32_SFLOAT + FORMAT_R32G32_SFLOAT Format = C.VK_FORMAT_R32G32_SFLOAT + FORMAT_R32G32B32_SFLOAT Format = C.VK_FORMAT_R32G32B32_SFLOAT + FORMAT_R32G32B32A32_SFLOAT Format = C.VK_FORMAT_R32G32B32A32_SFLOAT + + FORMAT_FEATURE_COLOR_ATTACHMENT_BIT FormatFeatureFlags = C.VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT + FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT FormatFeatureFlags = C.VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT + FORMAT_FEATURE_SAMPLED_IMAGE_BIT FormatFeatureFlags = C.VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT + + IMAGE_USAGE_SAMPLED_BIT ImageUsageFlags = C.VK_IMAGE_USAGE_SAMPLED_BIT + IMAGE_USAGE_COLOR_ATTACHMENT_BIT ImageUsageFlags = C.VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT + IMAGE_USAGE_STORAGE_BIT ImageUsageFlags = C.VK_IMAGE_USAGE_STORAGE_BIT + IMAGE_USAGE_TRANSFER_DST_BIT ImageUsageFlags = C.VK_IMAGE_USAGE_TRANSFER_DST_BIT + IMAGE_USAGE_TRANSFER_SRC_BIT ImageUsageFlags = C.VK_IMAGE_USAGE_TRANSFER_SRC_BIT + + FILTER_NEAREST Filter = C.VK_FILTER_NEAREST + FILTER_LINEAR Filter = C.VK_FILTER_LINEAR + + ATTACHMENT_LOAD_OP_CLEAR AttachmentLoadOp = C.VK_ATTACHMENT_LOAD_OP_CLEAR + ATTACHMENT_LOAD_OP_DONT_CARE AttachmentLoadOp = C.VK_ATTACHMENT_LOAD_OP_DONT_CARE + ATTACHMENT_LOAD_OP_LOAD AttachmentLoadOp = C.VK_ATTACHMENT_LOAD_OP_LOAD + + IMAGE_LAYOUT_UNDEFINED ImageLayout = C.VK_IMAGE_LAYOUT_UNDEFINED + IMAGE_LAYOUT_PRESENT_SRC_KHR ImageLayout = C.VK_IMAGE_LAYOUT_PRESENT_SRC_KHR + IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ImageLayout = C.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL + IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL ImageLayout = C.VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL + IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ImageLayout = C.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL + IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ImageLayout = C.VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL + IMAGE_LAYOUT_GENERAL ImageLayout = C.VK_IMAGE_LAYOUT_GENERAL + + BUFFER_USAGE_TRANSFER_DST_BIT BufferUsageFlags = C.VK_BUFFER_USAGE_TRANSFER_DST_BIT + BUFFER_USAGE_TRANSFER_SRC_BIT BufferUsageFlags = C.VK_BUFFER_USAGE_TRANSFER_SRC_BIT + BUFFER_USAGE_UNIFORM_BUFFER_BIT BufferUsageFlags = C.VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT + BUFFER_USAGE_STORAGE_BUFFER_BIT BufferUsageFlags = C.VK_BUFFER_USAGE_STORAGE_BUFFER_BIT + BUFFER_USAGE_INDEX_BUFFER_BIT BufferUsageFlags = C.VK_BUFFER_USAGE_INDEX_BUFFER_BIT + BUFFER_USAGE_VERTEX_BUFFER_BIT BufferUsageFlags = C.VK_BUFFER_USAGE_VERTEX_BUFFER_BIT + + ERROR_OUT_OF_DATE_KHR = Error(C.VK_ERROR_OUT_OF_DATE_KHR) + ERROR_SURFACE_LOST_KHR = Error(C.VK_ERROR_SURFACE_LOST_KHR) + ERROR_DEVICE_LOST = Error(C.VK_ERROR_DEVICE_LOST) + SUBOPTIMAL_KHR = Error(C.VK_SUBOPTIMAL_KHR) + + BLEND_FACTOR_ZERO BlendFactor = C.VK_BLEND_FACTOR_ZERO + BLEND_FACTOR_ONE BlendFactor = C.VK_BLEND_FACTOR_ONE + BLEND_FACTOR_ONE_MINUS_SRC_ALPHA BlendFactor = C.VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA + BLEND_FACTOR_DST_COLOR BlendFactor = C.VK_BLEND_FACTOR_DST_COLOR + + PRIMITIVE_TOPOLOGY_TRIANGLE_LIST PrimitiveTopology = C.VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST + PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP PrimitiveTopology = C.VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP + + SHADER_STAGE_VERTEX_BIT ShaderStageFlags = C.VK_SHADER_STAGE_VERTEX_BIT + SHADER_STAGE_FRAGMENT_BIT ShaderStageFlags = C.VK_SHADER_STAGE_FRAGMENT_BIT + SHADER_STAGE_COMPUTE_BIT ShaderStageFlags = C.VK_SHADER_STAGE_COMPUTE_BIT + + DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER DescriptorType = C.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER + DESCRIPTOR_TYPE_UNIFORM_BUFFER DescriptorType = C.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER + DESCRIPTOR_TYPE_STORAGE_BUFFER DescriptorType = C.VK_DESCRIPTOR_TYPE_STORAGE_BUFFER + DESCRIPTOR_TYPE_STORAGE_IMAGE DescriptorType = C.VK_DESCRIPTOR_TYPE_STORAGE_IMAGE + + MEMORY_PROPERTY_DEVICE_LOCAL_BIT MemoryPropertyFlags = C.VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT + MEMORY_PROPERTY_HOST_VISIBLE_BIT MemoryPropertyFlags = C.VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT + MEMORY_PROPERTY_HOST_COHERENT_BIT MemoryPropertyFlags = C.VK_MEMORY_PROPERTY_HOST_COHERENT_BIT + + DEPENDENCY_BY_REGION_BIT DependencyFlags = C.VK_DEPENDENCY_BY_REGION_BIT + + PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT PipelineStageFlags = C.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT + PIPELINE_STAGE_TRANSFER_BIT PipelineStageFlags = C.VK_PIPELINE_STAGE_TRANSFER_BIT + PIPELINE_STAGE_FRAGMENT_SHADER_BIT PipelineStageFlags = C.VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT + PIPELINE_STAGE_COMPUTE_SHADER_BIT PipelineStageFlags = C.VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT + PIPELINE_STAGE_TOP_OF_PIPE_BIT PipelineStageFlags = C.VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT + PIPELINE_STAGE_HOST_BIT PipelineStageFlags = C.VK_PIPELINE_STAGE_HOST_BIT + PIPELINE_STAGE_VERTEX_INPUT_BIT PipelineStageFlags = C.VK_PIPELINE_STAGE_VERTEX_INPUT_BIT + PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT PipelineStageFlags = C.VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT + + ACCESS_MEMORY_READ_BIT AccessFlags = C.VK_ACCESS_MEMORY_READ_BIT + ACCESS_MEMORY_WRITE_BIT AccessFlags = C.VK_ACCESS_MEMORY_WRITE_BIT + ACCESS_TRANSFER_READ_BIT AccessFlags = C.VK_ACCESS_TRANSFER_READ_BIT + ACCESS_TRANSFER_WRITE_BIT AccessFlags = C.VK_ACCESS_TRANSFER_WRITE_BIT + ACCESS_SHADER_READ_BIT AccessFlags = C.VK_ACCESS_SHADER_READ_BIT + ACCESS_SHADER_WRITE_BIT AccessFlags = C.VK_ACCESS_SHADER_WRITE_BIT + ACCESS_COLOR_ATTACHMENT_READ_BIT AccessFlags = C.VK_ACCESS_COLOR_ATTACHMENT_READ_BIT + ACCESS_COLOR_ATTACHMENT_WRITE_BIT AccessFlags = C.VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT + ACCESS_HOST_READ_BIT AccessFlags = C.VK_ACCESS_HOST_READ_BIT + ACCESS_HOST_WRITE_BIT AccessFlags = C.VK_ACCESS_HOST_WRITE_BIT + ACCESS_VERTEX_ATTRIBUTE_READ_BIT AccessFlags = C.VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT + ACCESS_INDEX_READ_BIT AccessFlags = C.VK_ACCESS_INDEX_READ_BIT + + PIPELINE_BIND_POINT_COMPUTE PipelineBindPoint = C.VK_PIPELINE_BIND_POINT_COMPUTE + PIPELINE_BIND_POINT_GRAPHICS PipelineBindPoint = C.VK_PIPELINE_BIND_POINT_GRAPHICS + + INDEX_TYPE_UINT16 IndexType = C.VK_INDEX_TYPE_UINT16 + INDEX_TYPE_UINT32 IndexType = C.VK_INDEX_TYPE_UINT32 + + QUEUE_GRAPHICS_BIT QueueFlags = C.VK_QUEUE_GRAPHICS_BIT + QUEUE_COMPUTE_BIT QueueFlags = C.VK_QUEUE_COMPUTE_BIT +) + +var ( + once sync.Once + loadErr error + + loadFuncs []func(dlopen func(name string) *[0]byte) +) + +var funcs struct { + vkCreateInstance C.PFN_vkCreateInstance + vkDestroyInstance C.PFN_vkDestroyInstance + vkEnumeratePhysicalDevices C.PFN_vkEnumeratePhysicalDevices + vkGetPhysicalDeviceQueueFamilyProperties C.PFN_vkGetPhysicalDeviceQueueFamilyProperties + vkGetPhysicalDeviceFormatProperties C.PFN_vkGetPhysicalDeviceFormatProperties + vkCreateDevice C.PFN_vkCreateDevice + vkDestroyDevice C.PFN_vkDestroyDevice + vkGetDeviceQueue C.PFN_vkGetDeviceQueue + vkCreateImageView C.PFN_vkCreateImageView + vkDestroyImageView C.PFN_vkDestroyImageView + vkCreateFramebuffer C.PFN_vkCreateFramebuffer + vkDestroyFramebuffer C.PFN_vkDestroyFramebuffer + vkDeviceWaitIdle C.PFN_vkDeviceWaitIdle + vkQueueWaitIdle C.PFN_vkQueueWaitIdle + vkCreateSemaphore C.PFN_vkCreateSemaphore + vkDestroySemaphore C.PFN_vkDestroySemaphore + vkCreateRenderPass C.PFN_vkCreateRenderPass + vkDestroyRenderPass C.PFN_vkDestroyRenderPass + vkCreateCommandPool C.PFN_vkCreateCommandPool + vkDestroyCommandPool C.PFN_vkDestroyCommandPool + vkAllocateCommandBuffers C.PFN_vkAllocateCommandBuffers + vkFreeCommandBuffers C.PFN_vkFreeCommandBuffers + vkBeginCommandBuffer C.PFN_vkBeginCommandBuffer + vkEndCommandBuffer C.PFN_vkEndCommandBuffer + vkQueueSubmit C.PFN_vkQueueSubmit + vkCmdBeginRenderPass C.PFN_vkCmdBeginRenderPass + vkCmdEndRenderPass C.PFN_vkCmdEndRenderPass + vkCmdCopyBuffer C.PFN_vkCmdCopyBuffer + vkCmdCopyBufferToImage C.PFN_vkCmdCopyBufferToImage + vkCmdPipelineBarrier C.PFN_vkCmdPipelineBarrier + vkCmdPushConstants C.PFN_vkCmdPushConstants + vkCmdBindPipeline C.PFN_vkCmdBindPipeline + vkCmdBindVertexBuffers C.PFN_vkCmdBindVertexBuffers + vkCmdSetViewport C.PFN_vkCmdSetViewport + vkCmdBindIndexBuffer C.PFN_vkCmdBindIndexBuffer + vkCmdDraw C.PFN_vkCmdDraw + vkCmdDrawIndexed C.PFN_vkCmdDrawIndexed + vkCmdBindDescriptorSets C.PFN_vkCmdBindDescriptorSets + vkCmdCopyImageToBuffer C.PFN_vkCmdCopyImageToBuffer + vkCmdDispatch C.PFN_vkCmdDispatch + vkCreateImage C.PFN_vkCreateImage + vkDestroyImage C.PFN_vkDestroyImage + vkGetImageMemoryRequirements C.PFN_vkGetImageMemoryRequirements + vkAllocateMemory C.PFN_vkAllocateMemory + vkBindImageMemory C.PFN_vkBindImageMemory + vkFreeMemory C.PFN_vkFreeMemory + vkGetPhysicalDeviceMemoryProperties C.PFN_vkGetPhysicalDeviceMemoryProperties + vkCreateSampler C.PFN_vkCreateSampler + vkDestroySampler C.PFN_vkDestroySampler + vkCreateBuffer C.PFN_vkCreateBuffer + vkDestroyBuffer C.PFN_vkDestroyBuffer + vkGetBufferMemoryRequirements C.PFN_vkGetBufferMemoryRequirements + vkBindBufferMemory C.PFN_vkBindBufferMemory + vkCreateShaderModule C.PFN_vkCreateShaderModule + vkDestroyShaderModule C.PFN_vkDestroyShaderModule + vkCreateGraphicsPipelines C.PFN_vkCreateGraphicsPipelines + vkDestroyPipeline C.PFN_vkDestroyPipeline + vkCreatePipelineLayout C.PFN_vkCreatePipelineLayout + vkDestroyPipelineLayout C.PFN_vkDestroyPipelineLayout + vkCreateDescriptorSetLayout C.PFN_vkCreateDescriptorSetLayout + vkDestroyDescriptorSetLayout C.PFN_vkDestroyDescriptorSetLayout + vkMapMemory C.PFN_vkMapMemory + vkUnmapMemory C.PFN_vkUnmapMemory + vkResetCommandBuffer C.PFN_vkResetCommandBuffer + vkCreateDescriptorPool C.PFN_vkCreateDescriptorPool + vkDestroyDescriptorPool C.PFN_vkDestroyDescriptorPool + vkAllocateDescriptorSets C.PFN_vkAllocateDescriptorSets + vkFreeDescriptorSets C.PFN_vkFreeDescriptorSets + vkUpdateDescriptorSets C.PFN_vkUpdateDescriptorSets + vkResetDescriptorPool C.PFN_vkResetDescriptorPool + vkCmdCopyImage C.PFN_vkCmdCopyImage + vkCreateComputePipelines C.PFN_vkCreateComputePipelines + vkCreateFence C.PFN_vkCreateFence + vkDestroyFence C.PFN_vkDestroyFence + vkWaitForFences C.PFN_vkWaitForFences + vkResetFences C.PFN_vkResetFences + vkGetPhysicalDeviceProperties C.PFN_vkGetPhysicalDeviceProperties + + vkGetPhysicalDeviceSurfaceSupportKHR C.PFN_vkGetPhysicalDeviceSurfaceSupportKHR + vkDestroySurfaceKHR C.PFN_vkDestroySurfaceKHR + vkGetPhysicalDeviceSurfaceFormatsKHR C.PFN_vkGetPhysicalDeviceSurfaceFormatsKHR + vkGetPhysicalDeviceSurfacePresentModesKHR C.PFN_vkGetPhysicalDeviceSurfacePresentModesKHR + vkGetPhysicalDeviceSurfaceCapabilitiesKHR C.PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR + + vkCreateSwapchainKHR C.PFN_vkCreateSwapchainKHR + vkDestroySwapchainKHR C.PFN_vkDestroySwapchainKHR + vkGetSwapchainImagesKHR C.PFN_vkGetSwapchainImagesKHR + vkAcquireNextImageKHR C.PFN_vkAcquireNextImageKHR + vkQueuePresentKHR C.PFN_vkQueuePresentKHR +} + +var ( + nilSurface C.VkSurfaceKHR + nilSwapchain C.VkSwapchainKHR + nilSemaphore C.VkSemaphore + nilImageView C.VkImageView + nilRenderPass C.VkRenderPass + nilFramebuffer C.VkFramebuffer + nilCommandPool C.VkCommandPool + nilImage C.VkImage + nilDeviceMemory C.VkDeviceMemory + nilSampler C.VkSampler + nilBuffer C.VkBuffer + nilShaderModule C.VkShaderModule + nilPipeline C.VkPipeline + nilPipelineCache C.VkPipelineCache + nilPipelineLayout C.VkPipelineLayout + nilDescriptorSetLayout C.VkDescriptorSetLayout + nilDescriptorPool C.VkDescriptorPool + nilDescriptorSet C.VkDescriptorSet + nilFence C.VkFence +) + +func vkInit() error { + once.Do(func() { + var libName string + switch { + case runtime.GOOS == "android": + libName = "libvulkan.so" + default: + libName = "libvulkan.so.1" + } + lib := dlopen(libName) + if lib == nil { + loadErr = fmt.Errorf("vulkan: %s", C.GoString(C.dlerror())) + return + } + dlopen := func(name string) *[0]byte { + return (*[0]byte)(dlsym(lib, name)) + } + must := func(name string) *[0]byte { + ptr := dlopen(name) + if ptr != nil { + return ptr + } + if loadErr == nil { + loadErr = fmt.Errorf("vulkan: function %q not found: %s", name, C.GoString(C.dlerror())) + } + return nil + } + funcs.vkCreateInstance = must("vkCreateInstance") + funcs.vkDestroyInstance = must("vkDestroyInstance") + funcs.vkEnumeratePhysicalDevices = must("vkEnumeratePhysicalDevices") + funcs.vkGetPhysicalDeviceQueueFamilyProperties = must("vkGetPhysicalDeviceQueueFamilyProperties") + funcs.vkGetPhysicalDeviceFormatProperties = must("vkGetPhysicalDeviceFormatProperties") + funcs.vkCreateDevice = must("vkCreateDevice") + funcs.vkDestroyDevice = must("vkDestroyDevice") + funcs.vkGetDeviceQueue = must("vkGetDeviceQueue") + funcs.vkCreateImageView = must("vkCreateImageView") + funcs.vkDestroyImageView = must("vkDestroyImageView") + funcs.vkCreateFramebuffer = must("vkCreateFramebuffer") + funcs.vkDestroyFramebuffer = must("vkDestroyFramebuffer") + funcs.vkDeviceWaitIdle = must("vkDeviceWaitIdle") + funcs.vkQueueWaitIdle = must("vkQueueWaitIdle") + funcs.vkCreateSemaphore = must("vkCreateSemaphore") + funcs.vkDestroySemaphore = must("vkDestroySemaphore") + funcs.vkCreateRenderPass = must("vkCreateRenderPass") + funcs.vkDestroyRenderPass = must("vkDestroyRenderPass") + funcs.vkCreateCommandPool = must("vkCreateCommandPool") + funcs.vkDestroyCommandPool = must("vkDestroyCommandPool") + funcs.vkAllocateCommandBuffers = must("vkAllocateCommandBuffers") + funcs.vkFreeCommandBuffers = must("vkFreeCommandBuffers") + funcs.vkBeginCommandBuffer = must("vkBeginCommandBuffer") + funcs.vkEndCommandBuffer = must("vkEndCommandBuffer") + funcs.vkQueueSubmit = must("vkQueueSubmit") + funcs.vkCmdBeginRenderPass = must("vkCmdBeginRenderPass") + funcs.vkCmdEndRenderPass = must("vkCmdEndRenderPass") + funcs.vkCmdCopyBuffer = must("vkCmdCopyBuffer") + funcs.vkCmdCopyBufferToImage = must("vkCmdCopyBufferToImage") + funcs.vkCmdPipelineBarrier = must("vkCmdPipelineBarrier") + funcs.vkCmdPushConstants = must("vkCmdPushConstants") + funcs.vkCmdBindPipeline = must("vkCmdBindPipeline") + funcs.vkCmdBindVertexBuffers = must("vkCmdBindVertexBuffers") + funcs.vkCmdSetViewport = must("vkCmdSetViewport") + funcs.vkCmdBindIndexBuffer = must("vkCmdBindIndexBuffer") + funcs.vkCmdDraw = must("vkCmdDraw") + funcs.vkCmdDrawIndexed = must("vkCmdDrawIndexed") + funcs.vkCmdBindDescriptorSets = must("vkCmdBindDescriptorSets") + funcs.vkCmdCopyImageToBuffer = must("vkCmdCopyImageToBuffer") + funcs.vkCmdDispatch = must("vkCmdDispatch") + funcs.vkCreateImage = must("vkCreateImage") + funcs.vkDestroyImage = must("vkDestroyImage") + funcs.vkGetImageMemoryRequirements = must("vkGetImageMemoryRequirements") + funcs.vkAllocateMemory = must("vkAllocateMemory") + funcs.vkBindImageMemory = must("vkBindImageMemory") + funcs.vkFreeMemory = must("vkFreeMemory") + funcs.vkGetPhysicalDeviceMemoryProperties = must("vkGetPhysicalDeviceMemoryProperties") + funcs.vkCreateSampler = must("vkCreateSampler") + funcs.vkDestroySampler = must("vkDestroySampler") + funcs.vkCreateBuffer = must("vkCreateBuffer") + funcs.vkDestroyBuffer = must("vkDestroyBuffer") + funcs.vkGetBufferMemoryRequirements = must("vkGetBufferMemoryRequirements") + funcs.vkBindBufferMemory = must("vkBindBufferMemory") + funcs.vkCreateShaderModule = must("vkCreateShaderModule") + funcs.vkDestroyShaderModule = must("vkDestroyShaderModule") + funcs.vkCreateGraphicsPipelines = must("vkCreateGraphicsPipelines") + funcs.vkDestroyPipeline = must("vkDestroyPipeline") + funcs.vkCreatePipelineLayout = must("vkCreatePipelineLayout") + funcs.vkDestroyPipelineLayout = must("vkDestroyPipelineLayout") + funcs.vkCreateDescriptorSetLayout = must("vkCreateDescriptorSetLayout") + funcs.vkDestroyDescriptorSetLayout = must("vkDestroyDescriptorSetLayout") + funcs.vkMapMemory = must("vkMapMemory") + funcs.vkUnmapMemory = must("vkUnmapMemory") + funcs.vkResetCommandBuffer = must("vkResetCommandBuffer") + funcs.vkCreateDescriptorPool = must("vkCreateDescriptorPool") + funcs.vkDestroyDescriptorPool = must("vkDestroyDescriptorPool") + funcs.vkAllocateDescriptorSets = must("vkAllocateDescriptorSets") + funcs.vkFreeDescriptorSets = must("vkFreeDescriptorSets") + funcs.vkUpdateDescriptorSets = must("vkUpdateDescriptorSets") + funcs.vkResetDescriptorPool = must("vkResetDescriptorPool") + funcs.vkCmdCopyImage = must("vkCmdCopyImage") + funcs.vkCreateComputePipelines = must("vkCreateComputePipelines") + funcs.vkCreateFence = must("vkCreateFence") + funcs.vkDestroyFence = must("vkDestroyFence") + funcs.vkWaitForFences = must("vkWaitForFences") + funcs.vkResetFences = must("vkResetFences") + funcs.vkGetPhysicalDeviceProperties = must("vkGetPhysicalDeviceProperties") + + funcs.vkGetPhysicalDeviceSurfaceSupportKHR = dlopen("vkGetPhysicalDeviceSurfaceSupportKHR") + funcs.vkDestroySurfaceKHR = dlopen("vkDestroySurfaceKHR") + funcs.vkGetPhysicalDeviceSurfaceFormatsKHR = dlopen("vkGetPhysicalDeviceSurfaceFormatsKHR") + funcs.vkGetPhysicalDeviceSurfacePresentModesKHR = dlopen("vkGetPhysicalDeviceSurfacePresentModesKHR") + funcs.vkGetPhysicalDeviceSurfaceCapabilitiesKHR = dlopen("vkGetPhysicalDeviceSurfaceCapabilitiesKHR") + + funcs.vkCreateSwapchainKHR = dlopen("vkCreateSwapchainKHR") + funcs.vkDestroySwapchainKHR = dlopen("vkDestroySwapchainKHR") + funcs.vkGetSwapchainImagesKHR = dlopen("vkGetSwapchainImagesKHR") + funcs.vkAcquireNextImageKHR = dlopen("vkAcquireNextImageKHR") + funcs.vkQueuePresentKHR = dlopen("vkQueuePresentKHR") + + for _, f := range loadFuncs { + f(dlopen) + } + }) + return loadErr +} + +func CreateInstance(exts ...string) (Instance, error) { + if err := vkInit(); err != nil { + return nil, err + } + inf := C.VkInstanceCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + } + if len(exts) > 0 { + cexts := mallocCStringArr(exts) + defer freeCStringArr(cexts) + inf.enabledExtensionCount = C.uint32_t(len(exts)) + inf.ppEnabledExtensionNames = &cexts[0] + } + var inst Instance + if err := vkErr(C.vkCreateInstance(funcs.vkCreateInstance, inf, nil, &inst)); err != nil { + return nil, fmt.Errorf("vulkan: vkCreateInstance: %w", err) + } + return inst, nil +} + +func mallocCStringArr(s []string) []*C.char { + carr := make([]*C.char, len(s)) + for i, ext := range s { + carr[i] = C.CString(ext) + } + return carr +} + +func freeCStringArr(s []*C.char) { + for i := range s { + C.free(unsafe.Pointer(s[i])) + s[i] = nil + } +} + +func DestroyInstance(inst Instance) { + C.vkDestroyInstance(funcs.vkDestroyInstance, inst, nil) +} + +func GetPhysicalDeviceQueueFamilyProperties(pd PhysicalDevice) []QueueFamilyProperties { + var count C.uint32_t + C.vkGetPhysicalDeviceQueueFamilyProperties(funcs.vkGetPhysicalDeviceQueueFamilyProperties, pd, &count, nil) + if count == 0 { + return nil + } + queues := make([]C.VkQueueFamilyProperties, count) + C.vkGetPhysicalDeviceQueueFamilyProperties(funcs.vkGetPhysicalDeviceQueueFamilyProperties, pd, &count, &queues[0]) + return queues +} + +func EnumeratePhysicalDevices(inst Instance) ([]PhysicalDevice, error) { + var count C.uint32_t + if err := vkErr(C.vkEnumeratePhysicalDevices(funcs.vkEnumeratePhysicalDevices, inst, &count, nil)); err != nil { + return nil, fmt.Errorf("vulkan: vkEnumeratePhysicalDevices: %w", err) + } + if count == 0 { + return nil, nil + } + devs := make([]C.VkPhysicalDevice, count) + if err := vkErr(C.vkEnumeratePhysicalDevices(funcs.vkEnumeratePhysicalDevices, inst, &count, &devs[0])); err != nil { + return nil, fmt.Errorf("vulkan: vkEnumeratePhysicalDevices: %w", err) + } + return devs, nil +} + +func ChoosePhysicalDevice(inst Instance, surf Surface) (PhysicalDevice, int, error) { + devs, err := EnumeratePhysicalDevices(inst) + if err != nil { + return nil, 0, err + } + for _, pd := range devs { + var props C.VkPhysicalDeviceProperties + C.vkGetPhysicalDeviceProperties(funcs.vkGetPhysicalDeviceProperties, pd, &props) + // The lavapipe software implementation doesn't work well rendering to a surface. + // See https://gitlab.freedesktop.org/mesa/mesa/-/issues/5473. + if surf != 0 && props.deviceType == C.VK_PHYSICAL_DEVICE_TYPE_CPU { + continue + } + const caps = C.VK_QUEUE_GRAPHICS_BIT | C.VK_QUEUE_COMPUTE_BIT + queueIdx, ok, err := chooseQueue(pd, surf, caps) + if err != nil { + return nil, 0, err + } + if !ok { + continue + } + if surf != nilSurface { + _, fmtFound, err := chooseFormat(pd, surf) + if err != nil { + return nil, 0, err + } + _, modFound, err := choosePresentMode(pd, surf) + if err != nil { + return nil, 0, err + } + if !fmtFound || !modFound { + continue + } + } + return pd, queueIdx, nil + } + return nil, 0, errors.New("vulkan: no suitable device found") +} + +func CreateDeviceAndQueue(pd C.VkPhysicalDevice, queueIdx int, exts ...string) (Device, error) { + priority := C.float(1.0) + qinf := C.VkDeviceQueueCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + queueCount: 1, + queueFamilyIndex: C.uint32_t(queueIdx), + pQueuePriorities: &priority, + } + inf := C.VkDeviceCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + queueCreateInfoCount: 1, + enabledExtensionCount: C.uint32_t(len(exts)), + } + if len(exts) > 0 { + cexts := mallocCStringArr(exts) + defer freeCStringArr(cexts) + inf.ppEnabledExtensionNames = &cexts[0] + } + var dev Device + if err := vkErr(C.vkCreateDevice(funcs.vkCreateDevice, pd, inf, qinf, nil, &dev)); err != nil { + return nil, fmt.Errorf("vulkan: vkCreateDevice: %w", err) + } + return dev, nil +} + +func GetDeviceQueue(d Device, queueFamily, queueIndex int) Queue { + var queue Queue + C.vkGetDeviceQueue(funcs.vkGetDeviceQueue, d, C.uint32_t(queueFamily), C.uint32_t(queueIndex), &queue) + return queue +} + +func GetPhysicalDeviceSurfaceCapabilities(pd PhysicalDevice, surf Surface) (SurfaceCapabilities, error) { + var caps C.VkSurfaceCapabilitiesKHR + err := vkErr(C.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(funcs.vkGetPhysicalDeviceSurfaceCapabilitiesKHR, pd, surf, &caps)) + if err != nil { + return SurfaceCapabilities{}, fmt.Errorf("vulkan: vkGetPhysicalDeviceSurfaceCapabilitiesKHR: %w", err) + } + return caps, nil +} + +func CreateSwapchain(pd PhysicalDevice, d Device, surf Surface, width, height int, old Swapchain) (Swapchain, []Image, Format, error) { + caps, err := GetPhysicalDeviceSurfaceCapabilities(pd, surf) + if err != nil { + return nilSwapchain, nil, 0, err + } + mode, modeOK, err := choosePresentMode(pd, surf) + if err != nil { + return nilSwapchain, nil, 0, err + } + format, fmtOK, err := chooseFormat(pd, surf) + if err != nil { + return nilSwapchain, nil, 0, err + } + if !modeOK || !fmtOK { + // This shouldn't happen because CreateDeviceAndQueue found at least + // one valid format and present mode. + return nilSwapchain, nil, 0, errors.New("vulkan: no valid format and present mode found") + } + // Find supported alpha composite mode. It doesn't matter which one, because rendering is + // always opaque. + alphaComp := C.VkCompositeAlphaFlagBitsKHR(1) + for caps.supportedCompositeAlpha&C.VkCompositeAlphaFlagsKHR(alphaComp) == 0 { + alphaComp <<= 1 + } + trans := C.VkSurfaceTransformFlagBitsKHR(C.VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) + if caps.supportedTransforms&C.VkSurfaceTransformFlagsKHR(trans) == 0 { + return nilSwapchain, nil, 0, errors.New("vulkan: VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR not supported") + } + inf := C.VkSwapchainCreateInfoKHR{ + sType: C.VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + surface: surf, + minImageCount: caps.minImageCount, + imageFormat: format.format, + imageColorSpace: format.colorSpace, + imageExtent: C.VkExtent2D{width: C.uint32_t(width), height: C.uint32_t(height)}, + imageArrayLayers: 1, + imageUsage: C.VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + imageSharingMode: C.VK_SHARING_MODE_EXCLUSIVE, + preTransform: trans, + presentMode: mode, + compositeAlpha: C.VkCompositeAlphaFlagBitsKHR(alphaComp), + clipped: C.VK_TRUE, + oldSwapchain: old, + } + var swchain Swapchain + if err := vkErr(C.vkCreateSwapchainKHR(funcs.vkCreateSwapchainKHR, d, &inf, nil, &swchain)); err != nil { + return nilSwapchain, nil, 0, fmt.Errorf("vulkan: vkCreateSwapchainKHR: %w", err) + } + var count C.uint32_t + if err := vkErr(C.vkGetSwapchainImagesKHR(funcs.vkGetSwapchainImagesKHR, d, swchain, &count, nil)); err != nil { + DestroySwapchain(d, swchain) + return nilSwapchain, nil, 0, fmt.Errorf("vulkan: vkGetSwapchainImagesKHR: %w", err) + } + if count == 0 { + DestroySwapchain(d, swchain) + return nilSwapchain, nil, 0, errors.New("vulkan: vkGetSwapchainImagesKHR returned no images") + } + imgs := make([]Image, count) + if err := vkErr(C.vkGetSwapchainImagesKHR(funcs.vkGetSwapchainImagesKHR, d, swchain, &count, &imgs[0])); err != nil { + DestroySwapchain(d, swchain) + return nilSwapchain, nil, 0, fmt.Errorf("vulkan: vkGetSwapchainImagesKHR: %w", err) + } + return swchain, imgs, format.format, nil +} + +func DestroySwapchain(d Device, swchain Swapchain) { + C.vkDestroySwapchainKHR(funcs.vkDestroySwapchainKHR, d, swchain, nil) +} + +func AcquireNextImage(d Device, swchain Swapchain, sem Semaphore, fence Fence) (int, error) { + res := C.vkAcquireNextImageKHR(funcs.vkAcquireNextImageKHR, d, swchain, math.MaxUint64, sem, fence) + if err := vkErr(res.res); err != nil { + return 0, fmt.Errorf("vulkan: vkAcquireNextImageKHR: %w", err) + } + return int(res.uint), nil +} + +func PresentQueue(q Queue, swchain Swapchain, sem Semaphore, imgIdx int) error { + cidx := C.uint32_t(imgIdx) + inf := C.VkPresentInfoKHR{ + sType: C.VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + swapchainCount: 1, + pSwapchains: &swchain, + pImageIndices: &cidx, + } + if sem != nilSemaphore { + inf.waitSemaphoreCount = 1 + inf.pWaitSemaphores = &sem + } + if err := vkErr(C.vkQueuePresentKHR(funcs.vkQueuePresentKHR, q, inf)); err != nil { + return fmt.Errorf("vulkan: vkQueuePresentKHR: %w", err) + } + return nil +} + +func CreateImageView(d Device, img Image, format Format) (ImageView, error) { + inf := C.VkImageViewCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + image: img, + viewType: C.VK_IMAGE_VIEW_TYPE_2D, + format: format, + subresourceRange: C.VkImageSubresourceRange{ + aspectMask: C.VK_IMAGE_ASPECT_COLOR_BIT, + levelCount: C.VK_REMAINING_MIP_LEVELS, + layerCount: C.VK_REMAINING_ARRAY_LAYERS, + }, + } + var view C.VkImageView + if err := vkErr(C.vkCreateImageView(funcs.vkCreateImageView, d, &inf, nil, &view)); err != nil { + return nilImageView, fmt.Errorf("vulkan: vkCreateImageView: %w", err) + } + return view, nil +} + +func DestroyImageView(d Device, view ImageView) { + C.vkDestroyImageView(funcs.vkDestroyImageView, d, view, nil) +} + +func CreateRenderPass(d Device, format Format, loadOp AttachmentLoadOp, initialLayout, finalLayout ImageLayout, passDeps []SubpassDependency) (RenderPass, error) { + att := C.VkAttachmentDescription{ + format: format, + samples: C.VK_SAMPLE_COUNT_1_BIT, + loadOp: loadOp, + storeOp: C.VK_ATTACHMENT_STORE_OP_STORE, + stencilLoadOp: C.VK_ATTACHMENT_LOAD_OP_DONT_CARE, + stencilStoreOp: C.VK_ATTACHMENT_STORE_OP_DONT_CARE, + initialLayout: initialLayout, + finalLayout: finalLayout, + } + + ref := C.VkAttachmentReference{ + attachment: 0, + layout: C.VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + } + + sub := C.VkSubpassDescription{ + pipelineBindPoint: C.VK_PIPELINE_BIND_POINT_GRAPHICS, + colorAttachmentCount: 1, + pColorAttachments: &ref, + } + + inf := C.VkRenderPassCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + attachmentCount: 1, + pAttachments: &att, + subpassCount: 1, + } + if n := len(passDeps); n > 0 { + inf.dependencyCount = C.uint32_t(n) + inf.pDependencies = &passDeps[0] + } + + var pass RenderPass + if err := vkErr(C.vkCreateRenderPass(funcs.vkCreateRenderPass, d, inf, sub, nil, &pass)); err != nil { + return nilRenderPass, fmt.Errorf("vulkan: vkCreateRenderPass: %w", err) + } + return pass, nil +} + +func DestroyRenderPass(d Device, r RenderPass) { + C.vkDestroyRenderPass(funcs.vkDestroyRenderPass, d, r, nil) +} + +func CreateFramebuffer(d Device, rp RenderPass, view ImageView, width, height int) (Framebuffer, error) { + inf := C.VkFramebufferCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + renderPass: rp, + attachmentCount: 1, + pAttachments: &view, + width: C.uint32_t(width), + height: C.uint32_t(height), + layers: 1, + } + var fbo Framebuffer + if err := vkErr(C.vkCreateFramebuffer(funcs.vkCreateFramebuffer, d, inf, nil, &fbo)); err != nil { + return nilFramebuffer, fmt.Errorf("vulkan: vkCreateFramebuffer: %w", err) + } + return fbo, nil + +} + +func DestroyFramebuffer(d Device, f Framebuffer) { + C.vkDestroyFramebuffer(funcs.vkDestroyFramebuffer, d, f, nil) +} + +func DeviceWaitIdle(d Device) error { + if err := vkErr(C.vkDeviceWaitIdle(funcs.vkDeviceWaitIdle, d)); err != nil { + return fmt.Errorf("vulkan: vkDeviceWaitIdle: %w", err) + } + return nil +} + +func QueueWaitIdle(q Queue) error { + if err := vkErr(C.vkQueueWaitIdle(funcs.vkQueueWaitIdle, q)); err != nil { + return fmt.Errorf("vulkan: vkQueueWaitIdle: %w", err) + } + return nil +} + +func CreateSemaphore(d Device) (Semaphore, error) { + inf := C.VkSemaphoreCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + } + var sem Semaphore + err := vkErr(C.vkCreateSemaphore(funcs.vkCreateSemaphore, d, &inf, nil, &sem)) + if err != nil { + return nilSemaphore, fmt.Errorf("vulkan: vkCreateSemaphore: %w", err) + } + return sem, err +} + +func DestroySemaphore(d Device, sem Semaphore) { + C.vkDestroySemaphore(funcs.vkDestroySemaphore, d, sem, nil) +} + +func DestroyDevice(dev Device) { + C.vkDestroyDevice(funcs.vkDestroyDevice, dev, nil) +} + +func DestroySurface(inst Instance, s Surface) { + C.vkDestroySurfaceKHR(funcs.vkDestroySurfaceKHR, inst, s, nil) +} + +func CreateCommandPool(d Device, queueIndex int) (CommandPool, error) { + inf := C.VkCommandPoolCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + queueFamilyIndex: C.uint32_t(queueIndex), + flags: C.VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | C.VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, + } + + var pool CommandPool + if err := vkErr(C.vkCreateCommandPool(funcs.vkCreateCommandPool, d, &inf, nil, &pool)); err != nil { + return nilCommandPool, fmt.Errorf("vulkan: vkCreateCommandPool: %w", err) + } + return pool, nil +} + +func DestroyCommandPool(d Device, pool CommandPool) { + C.vkDestroyCommandPool(funcs.vkDestroyCommandPool, d, pool, nil) +} + +func AllocateCommandBuffer(d Device, pool CommandPool) (CommandBuffer, error) { + inf := C.VkCommandBufferAllocateInfo{ + sType: C.VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + commandPool: pool, + level: C.VK_COMMAND_BUFFER_LEVEL_PRIMARY, + commandBufferCount: 1, + } + + var buf CommandBuffer + if err := vkErr(C.vkAllocateCommandBuffers(funcs.vkAllocateCommandBuffers, d, &inf, &buf)); err != nil { + return nil, fmt.Errorf("vulkan: vkAllocateCommandBuffers: %w", err) + } + return buf, nil +} + +func FreeCommandBuffers(d Device, pool CommandPool, bufs ...CommandBuffer) { + if len(bufs) == 0 { + return + } + C.vkFreeCommandBuffers(funcs.vkFreeCommandBuffers, d, pool, C.uint32_t(len(bufs)), &bufs[0]) +} + +func BeginCommandBuffer(buf CommandBuffer) error { + inf := C.VkCommandBufferBeginInfo{ + sType: C.VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + flags: C.VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + } + if err := vkErr(C.vkBeginCommandBuffer(funcs.vkBeginCommandBuffer, buf, inf)); err != nil { + return fmt.Errorf("vulkan: vkBeginCommandBuffer: %w", err) + } + return nil +} + +func EndCommandBuffer(buf CommandBuffer) error { + if err := vkErr(C.vkEndCommandBuffer(funcs.vkEndCommandBuffer, buf)); err != nil { + return fmt.Errorf("vulkan: vkEndCommandBuffer: %w", err) + } + return nil +} + +func QueueSubmit(q Queue, buf CommandBuffer, waitSems []Semaphore, waitStages []PipelineStageFlags, sigSems []Semaphore, fence Fence) error { + inf := C.VkSubmitInfo{ + sType: C.VK_STRUCTURE_TYPE_SUBMIT_INFO, + commandBufferCount: 1, + pCommandBuffers: &buf, + } + if len(waitSems) > 0 { + if len(waitSems) != len(waitStages) { + panic("len(waitSems) != len(waitStages)") + } + inf.waitSemaphoreCount = C.uint32_t(len(waitSems)) + inf.pWaitSemaphores = &waitSems[0] + inf.pWaitDstStageMask = &waitStages[0] + } + if len(sigSems) > 0 { + inf.signalSemaphoreCount = C.uint32_t(len(sigSems)) + inf.pSignalSemaphores = &sigSems[0] + } + if err := vkErr(C.vkQueueSubmit(funcs.vkQueueSubmit, q, inf, fence)); err != nil { + return fmt.Errorf("vulkan: vkQueueSubmit: %w", err) + } + return nil +} + +func CmdBeginRenderPass(buf CommandBuffer, rp RenderPass, fbo Framebuffer, width, height int, clearCol [4]float32) { + cclearCol := [4]C.float{C.float(clearCol[0]), C.float(clearCol[1]), C.float(clearCol[2]), C.float(clearCol[3])} + inf := C.VkRenderPassBeginInfo{ + sType: C.VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + renderPass: rp, + framebuffer: fbo, + renderArea: C.VkRect2D{extent: C.VkExtent2D{width: C.uint32_t(width), height: C.uint32_t(height)}}, + clearValueCount: 1, + pClearValues: (*C.VkClearValue)(unsafe.Pointer(&cclearCol)), + } + C.vkCmdBeginRenderPass(funcs.vkCmdBeginRenderPass, buf, inf, C.VK_SUBPASS_CONTENTS_INLINE) +} + +func CmdEndRenderPass(buf CommandBuffer) { + C.vkCmdEndRenderPass(funcs.vkCmdEndRenderPass, buf) +} + +func CmdCopyBuffer(cmdBuf CommandBuffer, src, dst Buffer, srcOff, dstOff, size int) { + C.vkCmdCopyBuffer(funcs.vkCmdCopyBuffer, cmdBuf, src, dst, 1, &C.VkBufferCopy{ + srcOffset: C.VkDeviceSize(srcOff), + dstOffset: C.VkDeviceSize(dstOff), + size: C.VkDeviceSize(size), + }) +} + +func CmdCopyBufferToImage(cmdBuf CommandBuffer, src Buffer, dst Image, layout ImageLayout, copy BufferImageCopy) { + C.vkCmdCopyBufferToImage(funcs.vkCmdCopyBufferToImage, cmdBuf, src, dst, layout, 1, ©) +} + +func CmdPipelineBarrier(cmdBuf CommandBuffer, srcStage, dstStage PipelineStageFlags, flags DependencyFlags, memBarriers []MemoryBarrier, bufBarriers []BufferMemoryBarrier, imgBarriers []ImageMemoryBarrier) { + var memPtr *MemoryBarrier + if len(memBarriers) > 0 { + memPtr = &memBarriers[0] + } + var bufPtr *BufferMemoryBarrier + if len(bufBarriers) > 0 { + bufPtr = &bufBarriers[0] + } + var imgPtr *ImageMemoryBarrier + if len(imgBarriers) > 0 { + imgPtr = &imgBarriers[0] + } + C.vkCmdPipelineBarrier(funcs.vkCmdPipelineBarrier, cmdBuf, srcStage, dstStage, flags, + C.uint32_t(len(memBarriers)), memPtr, + C.uint32_t(len(bufBarriers)), bufPtr, + C.uint32_t(len(imgBarriers)), imgPtr) +} + +func CmdPushConstants(cmdBuf CommandBuffer, layout PipelineLayout, stages ShaderStageFlags, offset int, data []byte) { + if len(data) == 0 { + return + } + C.vkCmdPushConstants(funcs.vkCmdPushConstants, cmdBuf, layout, stages, C.uint32_t(offset), C.uint32_t(len(data)), unsafe.Pointer(&data[0])) +} + +func CmdBindPipeline(cmdBuf CommandBuffer, bindPoint PipelineBindPoint, pipe Pipeline) { + C.vkCmdBindPipeline(funcs.vkCmdBindPipeline, cmdBuf, bindPoint, pipe) +} + +func CmdBindVertexBuffers(cmdBuf CommandBuffer, first int, buffers []Buffer, sizes []DeviceSize) { + if len(buffers) == 0 { + return + } + C.vkCmdBindVertexBuffers(funcs.vkCmdBindVertexBuffers, cmdBuf, C.uint32_t(first), C.uint32_t(len(buffers)), &buffers[0], &sizes[0]) +} + +func CmdSetViewport(cmdBuf CommandBuffer, first int, viewports ...Viewport) { + if len(viewports) == 0 { + return + } + C.vkCmdSetViewport(funcs.vkCmdSetViewport, cmdBuf, C.uint32_t(first), C.uint32_t(len(viewports)), &viewports[0]) +} + +func CmdBindIndexBuffer(cmdBuf CommandBuffer, buffer Buffer, offset int, typ IndexType) { + C.vkCmdBindIndexBuffer(funcs.vkCmdBindIndexBuffer, cmdBuf, buffer, C.VkDeviceSize(offset), typ) +} + +func CmdDraw(cmdBuf CommandBuffer, vertCount, instCount, firstVert, firstInst int) { + C.vkCmdDraw(funcs.vkCmdDraw, cmdBuf, C.uint32_t(vertCount), C.uint32_t(instCount), C.uint32_t(firstVert), C.uint32_t(firstInst)) +} + +func CmdDrawIndexed(cmdBuf CommandBuffer, idxCount, instCount, firstIdx, vertOff, firstInst int) { + C.vkCmdDrawIndexed(funcs.vkCmdDrawIndexed, cmdBuf, C.uint32_t(idxCount), C.uint32_t(instCount), C.uint32_t(firstIdx), C.int32_t(vertOff), C.uint32_t(firstInst)) +} + +func GetPhysicalDeviceFormatProperties(physDev PhysicalDevice, format Format) FormatFeatureFlags { + var props C.VkFormatProperties + C.vkGetPhysicalDeviceFormatProperties(funcs.vkGetPhysicalDeviceFormatProperties, physDev, format, &props) + return FormatFeatureFlags(props.optimalTilingFeatures) +} + +func CmdBindDescriptorSets(cmdBuf CommandBuffer, point PipelineBindPoint, layout PipelineLayout, firstSet int, sets []DescriptorSet) { + C.vkCmdBindDescriptorSets(funcs.vkCmdBindDescriptorSets, cmdBuf, point, layout, C.uint32_t(firstSet), C.uint32_t(len(sets)), &sets[0], 0, nil) +} + +func CmdCopyImage(cmdBuf CommandBuffer, src Image, srcLayout ImageLayout, dst Image, dstLayout ImageLayout, regions []ImageCopy) { + if len(regions) == 0 { + return + } + C.vkCmdCopyImage(funcs.vkCmdCopyImage, cmdBuf, src, srcLayout, dst, dstLayout, C.uint32_t(len(regions)), ®ions[0]) +} + +func CmdCopyImageToBuffer(cmdBuf CommandBuffer, src Image, srcLayout ImageLayout, dst Buffer, regions []BufferImageCopy) { + if len(regions) == 0 { + return + } + C.vkCmdCopyImageToBuffer(funcs.vkCmdCopyImageToBuffer, cmdBuf, src, srcLayout, dst, C.uint32_t(len(regions)), ®ions[0]) +} + +func CmdDispatch(cmdBuf CommandBuffer, x, y, z int) { + C.vkCmdDispatch(funcs.vkCmdDispatch, cmdBuf, C.uint32_t(x), C.uint32_t(y), C.uint32_t(z)) +} + +func CreateImage(pd PhysicalDevice, d Device, format Format, width, height int, usage ImageUsageFlags) (Image, DeviceMemory, error) { + inf := C.VkImageCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + imageType: C.VK_IMAGE_TYPE_2D, + format: format, + extent: C.VkExtent3D{ + width: C.uint32_t(width), + height: C.uint32_t(height), + depth: 1, + }, + mipLevels: 1, + arrayLayers: 1, + samples: C.VK_SAMPLE_COUNT_1_BIT, + tiling: C.VK_IMAGE_TILING_OPTIMAL, + usage: usage, + initialLayout: C.VK_IMAGE_LAYOUT_UNDEFINED, + } + var img C.VkImage + if err := vkErr(C.vkCreateImage(funcs.vkCreateImage, d, &inf, nil, &img)); err != nil { + return nilImage, nilDeviceMemory, fmt.Errorf("vulkan: vkCreateImage: %w", err) + } + var memReqs C.VkMemoryRequirements + C.vkGetImageMemoryRequirements(funcs.vkGetImageMemoryRequirements, d, img, &memReqs) + + memIdx, found := findMemoryTypeIndex(pd, memReqs.memoryTypeBits, C.VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) + if !found { + DestroyImage(d, img) + return nilImage, nilDeviceMemory, errors.New("vulkan: no memory type suitable for images found") + } + + memInf := C.VkMemoryAllocateInfo{ + sType: C.VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + allocationSize: memReqs.size, + memoryTypeIndex: C.uint32_t(memIdx), + } + var imgMem C.VkDeviceMemory + if err := vkErr(C.vkAllocateMemory(funcs.vkAllocateMemory, d, &memInf, nil, &imgMem)); err != nil { + DestroyImage(d, img) + return nilImage, nilDeviceMemory, fmt.Errorf("vulkan: vkAllocateMemory: %w", err) + } + + if err := vkErr(C.vkBindImageMemory(funcs.vkBindImageMemory, d, img, imgMem, 0)); err != nil { + FreeMemory(d, imgMem) + DestroyImage(d, img) + return nilImage, nilDeviceMemory, fmt.Errorf("vulkan: vkBindImageMemory: %w", err) + } + return img, imgMem, nil +} + +func DestroyImage(d Device, img Image) { + C.vkDestroyImage(funcs.vkDestroyImage, d, img, nil) +} + +func FreeMemory(d Device, mem DeviceMemory) { + C.vkFreeMemory(funcs.vkFreeMemory, d, mem, nil) +} + +func CreateSampler(d Device, minFilter, magFilter Filter) (Sampler, error) { + inf := C.VkSamplerCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + minFilter: minFilter, + magFilter: magFilter, + addressModeU: C.VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + addressModeV: C.VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + } + var s C.VkSampler + if err := vkErr(C.vkCreateSampler(funcs.vkCreateSampler, d, &inf, nil, &s)); err != nil { + return nilSampler, fmt.Errorf("vulkan: vkCreateSampler: %w", err) + } + return s, nil +} + +func DestroySampler(d Device, sampler Sampler) { + C.vkDestroySampler(funcs.vkDestroySampler, d, sampler, nil) +} + +func CreateBuffer(pd PhysicalDevice, d Device, size int, usage BufferUsageFlags, props MemoryPropertyFlags) (Buffer, DeviceMemory, error) { + inf := C.VkBufferCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + size: C.VkDeviceSize(size), + usage: usage, + } + var buf C.VkBuffer + if err := vkErr(C.vkCreateBuffer(funcs.vkCreateBuffer, d, &inf, nil, &buf)); err != nil { + return nilBuffer, nilDeviceMemory, fmt.Errorf("vulkan: vkCreateBuffer: %w", err) + } + + var memReqs C.VkMemoryRequirements + C.vkGetBufferMemoryRequirements(funcs.vkGetBufferMemoryRequirements, d, buf, &memReqs) + + memIdx, found := findMemoryTypeIndex(pd, memReqs.memoryTypeBits, props) + if !found { + DestroyBuffer(d, buf) + return nilBuffer, nilDeviceMemory, errors.New("vulkan: no memory suitable for buffers found") + } + memInf := C.VkMemoryAllocateInfo{ + sType: C.VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + allocationSize: memReqs.size, + memoryTypeIndex: C.uint32_t(memIdx), + } + + var mem C.VkDeviceMemory + if err := vkErr(C.vkAllocateMemory(funcs.vkAllocateMemory, d, &memInf, nil, &mem)); err != nil { + DestroyBuffer(d, buf) + return nilBuffer, nilDeviceMemory, fmt.Errorf("vulkan: vkAllocateMemory: %w", err) + } + + if err := vkErr(C.vkBindBufferMemory(funcs.vkBindBufferMemory, d, buf, mem, 0)); err != nil { + FreeMemory(d, mem) + DestroyBuffer(d, buf) + return nilBuffer, nilDeviceMemory, fmt.Errorf("vulkan: vkBindBufferMemory: %w", err) + } + return buf, mem, nil +} + +func DestroyBuffer(d Device, buf Buffer) { + C.vkDestroyBuffer(funcs.vkDestroyBuffer, d, buf, nil) +} + +func CreateShaderModule(d Device, spirv string) (ShaderModule, error) { + ptr := unsafe.Pointer((*reflect.StringHeader)(unsafe.Pointer(&spirv)).Data) + inf := C.VkShaderModuleCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + codeSize: C.size_t(len(spirv)), + pCode: (*C.uint32_t)(ptr), + } + + var mod C.VkShaderModule + if err := vkErr(C.vkCreateShaderModule(funcs.vkCreateShaderModule, d, inf, nil, &mod)); err != nil { + return nilShaderModule, fmt.Errorf("vulkan: vkCreateShaderModule: %w", err) + } + return mod, nil +} + +func DestroyShaderModule(d Device, mod ShaderModule) { + C.vkDestroyShaderModule(funcs.vkDestroyShaderModule, d, mod, nil) +} + +func CreateGraphicsPipeline(d Device, pass RenderPass, vmod, fmod ShaderModule, blend bool, srcFactor, dstFactor BlendFactor, topology PrimitiveTopology, bindings []VertexInputBindingDescription, attrs []VertexInputAttributeDescription, layout PipelineLayout) (Pipeline, error) { + main := C.CString("main") + defer C.free(unsafe.Pointer(main)) + stages := []C.VkPipelineShaderStageCreateInfo{ + { + sType: C.VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + stage: C.VK_SHADER_STAGE_VERTEX_BIT, + module: vmod, + pName: main, + }, + { + sType: C.VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + stage: C.VK_SHADER_STAGE_FRAGMENT_BIT, + module: fmod, + pName: main, + }, + } + dynStates := []C.VkDynamicState{C.VK_DYNAMIC_STATE_VIEWPORT} + dynInf := C.VkPipelineDynamicStateCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + dynamicStateCount: C.uint32_t(len(dynStates)), + pDynamicStates: &dynStates[0], + } + const maxDim = 0x7fffffff + scissors := []C.VkRect2D{{extent: C.VkExtent2D{width: maxDim, height: maxDim}}} + viewportInf := C.VkPipelineViewportStateCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + viewportCount: 1, + scissorCount: C.uint32_t(len(scissors)), + pScissors: &scissors[0], + } + enable := C.VkBool32(0) + if blend { + enable = 1 + } + attBlendInf := C.VkPipelineColorBlendAttachmentState{ + blendEnable: enable, + srcColorBlendFactor: srcFactor, + srcAlphaBlendFactor: srcFactor, + dstColorBlendFactor: dstFactor, + dstAlphaBlendFactor: dstFactor, + colorBlendOp: C.VK_BLEND_OP_ADD, + alphaBlendOp: C.VK_BLEND_OP_ADD, + colorWriteMask: C.VK_COLOR_COMPONENT_R_BIT | C.VK_COLOR_COMPONENT_G_BIT | C.VK_COLOR_COMPONENT_B_BIT | C.VK_COLOR_COMPONENT_A_BIT, + } + blendInf := C.VkPipelineColorBlendStateCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + attachmentCount: 1, + pAttachments: &attBlendInf, + } + var vkBinds []C.VkVertexInputBindingDescription + var vkAttrs []C.VkVertexInputAttributeDescription + for _, b := range bindings { + vkBinds = append(vkBinds, C.VkVertexInputBindingDescription{ + binding: C.uint32_t(b.Binding), + stride: C.uint32_t(b.Stride), + }) + } + for _, a := range attrs { + vkAttrs = append(vkAttrs, C.VkVertexInputAttributeDescription{ + location: C.uint32_t(a.Location), + binding: C.uint32_t(a.Binding), + format: a.Format, + offset: C.uint32_t(a.Offset), + }) + } + vertexInf := C.VkPipelineVertexInputStateCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + } + if n := len(vkBinds); n > 0 { + vertexInf.vertexBindingDescriptionCount = C.uint32_t(n) + vertexInf.pVertexBindingDescriptions = &vkBinds[0] + } + if n := len(vkAttrs); n > 0 { + vertexInf.vertexAttributeDescriptionCount = C.uint32_t(n) + vertexInf.pVertexAttributeDescriptions = &vkAttrs[0] + } + inf := C.VkGraphicsPipelineCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + stageCount: C.uint32_t(len(stages)), + pStages: &stages[0], + renderPass: pass, + layout: layout, + pRasterizationState: &C.VkPipelineRasterizationStateCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + lineWidth: 1.0, + }, + pMultisampleState: &C.VkPipelineMultisampleStateCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + rasterizationSamples: C.VK_SAMPLE_COUNT_1_BIT, + }, + pInputAssemblyState: &C.VkPipelineInputAssemblyStateCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + topology: topology, + }, + } + + var pipe C.VkPipeline + if err := vkErr(C.vkCreateGraphicsPipelines(funcs.vkCreateGraphicsPipelines, d, nilPipelineCache, inf, dynInf, blendInf, vertexInf, viewportInf, nil, &pipe)); err != nil { + return nilPipeline, fmt.Errorf("vulkan: vkCreateGraphicsPipelines: %w", err) + } + return pipe, nil +} + +func DestroyPipeline(d Device, p Pipeline) { + C.vkDestroyPipeline(funcs.vkDestroyPipeline, d, p, nil) +} + +func CreatePipelineLayout(d Device, pushRanges []PushConstantRange, sets []DescriptorSetLayout) (PipelineLayout, error) { + inf := C.VkPipelineLayoutCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + } + if n := len(sets); n > 0 { + inf.setLayoutCount = C.uint32_t(n) + inf.pSetLayouts = &sets[0] + } + if n := len(pushRanges); n > 0 { + inf.pushConstantRangeCount = C.uint32_t(n) + inf.pPushConstantRanges = &pushRanges[0] + } + var l C.VkPipelineLayout + if err := vkErr(C.vkCreatePipelineLayout(funcs.vkCreatePipelineLayout, d, inf, nil, &l)); err != nil { + return nilPipelineLayout, fmt.Errorf("vulkan: vkCreatePipelineLayout: %w", err) + } + return l, nil +} + +func DestroyPipelineLayout(d Device, l PipelineLayout) { + C.vkDestroyPipelineLayout(funcs.vkDestroyPipelineLayout, d, l, nil) +} + +func CreateDescriptorSetLayout(d Device, bindings []DescriptorSetLayoutBinding) (DescriptorSetLayout, error) { + var vkbinds []C.VkDescriptorSetLayoutBinding + for _, b := range bindings { + vkbinds = append(vkbinds, C.VkDescriptorSetLayoutBinding{ + binding: C.uint32_t(b.Binding), + descriptorType: b.DescriptorType, + descriptorCount: 1, + stageFlags: b.StageFlags, + }) + } + inf := C.VkDescriptorSetLayoutCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + } + if n := len(vkbinds); n > 0 { + inf.bindingCount = C.uint32_t(len(vkbinds)) + inf.pBindings = &vkbinds[0] + } + var l C.VkDescriptorSetLayout + if err := vkErr(C.vkCreateDescriptorSetLayout(funcs.vkCreateDescriptorSetLayout, d, inf, nil, &l)); err != nil { + return nilDescriptorSetLayout, fmt.Errorf("vulkan: vkCreateDescriptorSetLayout: %w", err) + } + return l, nil +} + +func DestroyDescriptorSetLayout(d Device, l DescriptorSetLayout) { + C.vkDestroyDescriptorSetLayout(funcs.vkDestroyDescriptorSetLayout, d, l, nil) +} + +func MapMemory(d Device, mem DeviceMemory, offset, size int) ([]byte, error) { + var ptr unsafe.Pointer + if err := vkErr(C.vkMapMemory(funcs.vkMapMemory, d, mem, C.VkDeviceSize(offset), C.VkDeviceSize(size), 0, &ptr)); err != nil { + return nil, fmt.Errorf("vulkan: vkMapMemory: %w", err) + } + return ((*[1 << 30]byte)(ptr))[:size:size], nil +} + +func UnmapMemory(d Device, mem DeviceMemory) { + C.vkUnmapMemory(funcs.vkUnmapMemory, d, mem) +} + +func ResetCommandBuffer(buf CommandBuffer) error { + if err := vkErr(C.vkResetCommandBuffer(funcs.vkResetCommandBuffer, buf, 0)); err != nil { + return fmt.Errorf("vulkan: vkResetCommandBuffer. %w", err) + } + return nil +} + +func CreateDescriptorPool(d Device, maxSets int, sizes []DescriptorPoolSize) (DescriptorPool, error) { + inf := C.VkDescriptorPoolCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + maxSets: C.uint32_t(maxSets), + poolSizeCount: C.uint32_t(len(sizes)), + pPoolSizes: &sizes[0], + } + var pool C.VkDescriptorPool + if err := vkErr(C.vkCreateDescriptorPool(funcs.vkCreateDescriptorPool, d, inf, nil, &pool)); err != nil { + return nilDescriptorPool, fmt.Errorf("vulkan: vkCreateDescriptorPool: %w", err) + } + return pool, nil +} + +func DestroyDescriptorPool(d Device, pool DescriptorPool) { + C.vkDestroyDescriptorPool(funcs.vkDestroyDescriptorPool, d, pool, nil) +} + +func ResetDescriptorPool(d Device, pool DescriptorPool) error { + if err := vkErr(C.vkResetDescriptorPool(funcs.vkResetDescriptorPool, d, pool, 0)); err != nil { + return fmt.Errorf("vulkan: vkResetDescriptorPool: %w", err) + } + return nil +} + +func UpdateDescriptorSet(d Device, write WriteDescriptorSet) { + C.vkUpdateDescriptorSets(funcs.vkUpdateDescriptorSets, d, write, 0, nil) +} + +func AllocateDescriptorSet(d Device, pool DescriptorPool, layout DescriptorSetLayout) (DescriptorSet, error) { + inf := C.VkDescriptorSetAllocateInfo{ + sType: C.VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + descriptorPool: pool, + descriptorSetCount: 1, + pSetLayouts: &layout, + } + var set C.VkDescriptorSet + if err := vkErr(C.vkAllocateDescriptorSets(funcs.vkAllocateDescriptorSets, d, inf, &set)); err != nil { + return nilDescriptorSet, fmt.Errorf("vulkan: vkAllocateDescriptorSets: %w", err) + } + return set, nil +} + +func CreateComputePipeline(d Device, mod ShaderModule, layout PipelineLayout) (Pipeline, error) { + main := C.CString("main") + defer C.free(unsafe.Pointer(main)) + inf := C.VkComputePipelineCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, + stage: C.VkPipelineShaderStageCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + stage: C.VK_SHADER_STAGE_COMPUTE_BIT, + module: mod, + pName: main, + }, + layout: layout, + } + var pipe C.VkPipeline + if err := vkErr(C.vkCreateComputePipelines(funcs.vkCreateComputePipelines, d, nilPipelineCache, 1, &inf, nil, &pipe)); err != nil { + return nilPipeline, fmt.Errorf("vulkan: vkCreateComputePipelines: %w", err) + } + return pipe, nil +} + +func CreateFence(d Device) (Fence, error) { + inf := C.VkFenceCreateInfo{ + sType: C.VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + } + var f C.VkFence + if err := vkErr(C.vkCreateFence(funcs.vkCreateFence, d, &inf, nil, &f)); err != nil { + return nilFence, fmt.Errorf("vulkan: vkCreateFence: %w", err) + } + return f, nil +} + +func DestroyFence(d Device, f Fence) { + C.vkDestroyFence(funcs.vkDestroyFence, d, f, nil) +} + +func WaitForFences(d Device, fences ...Fence) error { + if len(fences) == 0 { + return nil + } + err := vkErr(C.vkWaitForFences(funcs.vkWaitForFences, d, C.uint32_t(len(fences)), &fences[0], C.VK_TRUE, 0xffffffffffffffff)) + if err != nil { + return fmt.Errorf("vulkan: vkWaitForFences: %w", err) + } + return nil +} + +func ResetFences(d Device, fences ...Fence) error { + if len(fences) == 0 { + return nil + } + err := vkErr(C.vkResetFences(funcs.vkResetFences, d, C.uint32_t(len(fences)), &fences[0])) + if err != nil { + return fmt.Errorf("vulkan: vkResetFences: %w", err) + } + return nil +} + +func BuildSubpassDependency(srcStage, dstStage PipelineStageFlags, srcMask, dstMask AccessFlags, flags DependencyFlags) SubpassDependency { + return C.VkSubpassDependency{ + srcSubpass: C.VK_SUBPASS_EXTERNAL, + srcStageMask: srcStage, + srcAccessMask: srcMask, + dstSubpass: 0, + dstStageMask: dstStage, + dstAccessMask: dstMask, + dependencyFlags: flags, + } +} + +func BuildPushConstantRange(stages ShaderStageFlags, offset, size int) PushConstantRange { + return C.VkPushConstantRange{ + stageFlags: stages, + offset: C.uint32_t(offset), + size: C.uint32_t(size), + } +} + +func BuildDescriptorPoolSize(typ DescriptorType, count int) DescriptorPoolSize { + return C.VkDescriptorPoolSize{ + _type: typ, + descriptorCount: C.uint32_t(count), + } +} + +func BuildWriteDescriptorSetImage(set DescriptorSet, binding int, typ DescriptorType, sampler Sampler, view ImageView, layout ImageLayout) WriteDescriptorSet { + return C.VkWriteDescriptorSet{ + sType: C.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + dstSet: set, + dstBinding: C.uint32_t(binding), + descriptorCount: 1, + descriptorType: typ, + pImageInfo: &C.VkDescriptorImageInfo{ + sampler: sampler, + imageView: view, + imageLayout: layout, + }, + } +} + +func BuildWriteDescriptorSetBuffer(set DescriptorSet, binding int, typ DescriptorType, buf Buffer) WriteDescriptorSet { + return C.VkWriteDescriptorSet{ + sType: C.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + dstSet: set, + dstBinding: C.uint32_t(binding), + descriptorCount: 1, + descriptorType: typ, + pBufferInfo: &C.VkDescriptorBufferInfo{ + buffer: buf, + _range: C.VK_WHOLE_SIZE, + }, + } +} + +func (r PushConstantRange) StageFlags() ShaderStageFlags { + return r.stageFlags +} + +func (r PushConstantRange) Offset() int { + return int(r.offset) +} + +func (r PushConstantRange) Size() int { + return int(r.size) +} + +func (p QueueFamilyProperties) Flags() QueueFlags { + return p.queueFlags +} + +func (c SurfaceCapabilities) MinExtent() image.Point { + return image.Pt(int(c.minImageExtent.width), int(c.minImageExtent.height)) +} + +func (c SurfaceCapabilities) MaxExtent() image.Point { + return image.Pt(int(c.maxImageExtent.width), int(c.maxImageExtent.height)) +} + +func BuildViewport(x, y, width, height float32) Viewport { + return C.VkViewport{ + x: C.float(x), + y: C.float(y), + width: C.float(width), + height: C.float(height), + maxDepth: 1.0, + } +} + +func BuildImageMemoryBarrier(img Image, srcMask, dstMask AccessFlags, oldLayout, newLayout ImageLayout) ImageMemoryBarrier { + return C.VkImageMemoryBarrier{ + sType: C.VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + srcAccessMask: srcMask, + dstAccessMask: dstMask, + oldLayout: oldLayout, + newLayout: newLayout, + image: img, + subresourceRange: C.VkImageSubresourceRange{ + aspectMask: C.VK_IMAGE_ASPECT_COLOR_BIT, + levelCount: C.VK_REMAINING_MIP_LEVELS, + layerCount: C.VK_REMAINING_ARRAY_LAYERS, + }, + } +} + +func BuildBufferMemoryBarrier(buf Buffer, srcMask, dstMask AccessFlags) BufferMemoryBarrier { + return C.VkBufferMemoryBarrier{ + sType: C.VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, + srcAccessMask: srcMask, + dstAccessMask: dstMask, + buffer: buf, + size: C.VK_WHOLE_SIZE, + } +} + +func BuildMemoryBarrier(srcMask, dstMask AccessFlags) MemoryBarrier { + return C.VkMemoryBarrier{ + sType: C.VK_STRUCTURE_TYPE_MEMORY_BARRIER, + srcAccessMask: srcMask, + dstAccessMask: dstMask, + } +} + +func BuildBufferImageCopy(bufOff, bufStride, x, y, width, height int) BufferImageCopy { + return C.VkBufferImageCopy{ + bufferOffset: C.VkDeviceSize(bufOff), + bufferRowLength: C.uint32_t(bufStride), + imageSubresource: C.VkImageSubresourceLayers{ + aspectMask: C.VK_IMAGE_ASPECT_COLOR_BIT, + layerCount: 1, + }, + imageOffset: C.VkOffset3D{ + x: C.int32_t(x), y: C.int32_t(y), z: 0, + }, + imageExtent: C.VkExtent3D{ + width: C.uint32_t(width), height: C.uint32_t(height), depth: 1, + }, + } +} + +func BuildImageCopy(srcX, srcY, dstX, dstY, width, height int) ImageCopy { + return C.VkImageCopy{ + srcSubresource: C.VkImageSubresourceLayers{ + aspectMask: C.VK_IMAGE_ASPECT_COLOR_BIT, + layerCount: 1, + }, + srcOffset: C.VkOffset3D{ + x: C.int32_t(srcX), + y: C.int32_t(srcY), + }, + dstSubresource: C.VkImageSubresourceLayers{ + aspectMask: C.VK_IMAGE_ASPECT_COLOR_BIT, + layerCount: 1, + }, + dstOffset: C.VkOffset3D{ + x: C.int32_t(dstX), + y: C.int32_t(dstY), + }, + extent: C.VkExtent3D{ + width: C.uint32_t(width), + height: C.uint32_t(height), + depth: 1, + }, + } +} + +func findMemoryTypeIndex(pd C.VkPhysicalDevice, constraints C.uint32_t, wantProps C.VkMemoryPropertyFlags) (int, bool) { + var memProps C.VkPhysicalDeviceMemoryProperties + C.vkGetPhysicalDeviceMemoryProperties(funcs.vkGetPhysicalDeviceMemoryProperties, pd, &memProps) + + for i := 0; i < int(memProps.memoryTypeCount); i++ { + if (constraints & (1 << i)) == 0 { + continue + } + if (memProps.memoryTypes[i].propertyFlags & wantProps) != wantProps { + continue + } + return i, true + } + + return 0, false +} + +func choosePresentMode(pd C.VkPhysicalDevice, surf Surface) (C.VkPresentModeKHR, bool, error) { + var count C.uint32_t + err := vkErr(C.vkGetPhysicalDeviceSurfacePresentModesKHR(funcs.vkGetPhysicalDeviceSurfacePresentModesKHR, pd, surf, &count, nil)) + if err != nil { + return 0, false, fmt.Errorf("vulkan: vkGetPhysicalDeviceSurfacePresentModesKHR: %w", err) + } + if count == 0 { + return 0, false, nil + } + modes := make([]C.VkPresentModeKHR, count) + err = vkErr(C.vkGetPhysicalDeviceSurfacePresentModesKHR(funcs.vkGetPhysicalDeviceSurfacePresentModesKHR, pd, surf, &count, &modes[0])) + if err != nil { + return 0, false, fmt.Errorf("vulkan: kGetPhysicalDeviceSurfacePresentModesKHR: %w", err) + } + for _, m := range modes { + if m == C.VK_PRESENT_MODE_MAILBOX_KHR || m == C.VK_PRESENT_MODE_FIFO_KHR { + return m, true, nil + } + } + return 0, false, nil +} + +func chooseFormat(pd C.VkPhysicalDevice, surf Surface) (C.VkSurfaceFormatKHR, bool, error) { + var count C.uint32_t + err := vkErr(C.vkGetPhysicalDeviceSurfaceFormatsKHR(funcs.vkGetPhysicalDeviceSurfaceFormatsKHR, pd, surf, &count, nil)) + if err != nil { + return C.VkSurfaceFormatKHR{}, false, fmt.Errorf("vulkan: vkGetPhysicalDeviceSurfaceFormatsKHR: %w", err) + } + if count == 0 { + return C.VkSurfaceFormatKHR{}, false, nil + } + formats := make([]C.VkSurfaceFormatKHR, count) + err = vkErr(C.vkGetPhysicalDeviceSurfaceFormatsKHR(funcs.vkGetPhysicalDeviceSurfaceFormatsKHR, pd, surf, &count, &formats[0])) + if err != nil { + return C.VkSurfaceFormatKHR{}, false, fmt.Errorf("vulkan: vkGetPhysicalDeviceSurfaceFormatsKHR: %w", err) + } + // Query for format with sRGB support. + // TODO: Support devices without sRGB. + for _, f := range formats { + if f.colorSpace != C.VK_COLOR_SPACE_SRGB_NONLINEAR_KHR { + continue + } + switch f.format { + case C.VK_FORMAT_B8G8R8A8_SRGB, C.VK_FORMAT_R8G8B8A8_SRGB: + return f, true, nil + } + } + return C.VkSurfaceFormatKHR{}, false, nil +} + +func chooseQueue(pd C.VkPhysicalDevice, surf Surface, flags C.VkQueueFlags) (int, bool, error) { + queues := GetPhysicalDeviceQueueFamilyProperties(pd) + for i, q := range queues { + // Check for presentation and feature support. + if q.queueFlags&flags != flags { + continue + } + if surf != nilSurface { + // Check for presentation support. It is possible that a device has no + // queue with both rendering and presentation support, but not in reality. + // See https://github.com/KhronosGroup/Vulkan-Docs/issues/1234. + var support C.VkBool32 + if err := vkErr(C.vkGetPhysicalDeviceSurfaceSupportKHR(funcs.vkGetPhysicalDeviceSurfaceSupportKHR, pd, C.uint32_t(i), surf, &support)); err != nil { + return 0, false, fmt.Errorf("vulkan: vkGetPhysicalDeviceSurfaceSupportKHR: %w", err) + } + if support != C.VK_TRUE { + continue + } + } + return i, true, nil + } + return 0, false, nil +} + +func dlsym(handle unsafe.Pointer, s string) unsafe.Pointer { + cs := C.CString(s) + defer C.free(unsafe.Pointer(cs)) + return C.dlsym(handle, cs) +} + +func dlopen(lib string) unsafe.Pointer { + clib := C.CString(lib) + defer C.free(unsafe.Pointer(clib)) + return C.dlopen(clib, C.RTLD_NOW|C.RTLD_LOCAL) +} + +func vkErr(res C.VkResult) error { + switch res { + case C.VK_SUCCESS: + return nil + default: + return Error(res) + } +} + +func (e Error) Error() string { + return fmt.Sprintf("error %d", e) +} diff --git a/vendor/gioui.org/internal/vk/vulkan_android.go b/vendor/gioui.org/internal/vk/vulkan_android.go new file mode 100644 index 0000000..143146e --- /dev/null +++ b/vendor/gioui.org/internal/vk/vulkan_android.go @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build !nowayland +// +build !nowayland + +package vk + +/* +#define VK_USE_PLATFORM_ANDROID_KHR +#define VK_NO_PROTOTYPES 1 +#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object; +#include <android/native_window.h> +#include <vulkan/vulkan.h> + +static VkResult vkCreateAndroidSurfaceKHR(PFN_vkCreateAndroidSurfaceKHR f, VkInstance instance, const VkAndroidSurfaceCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) { + return f(instance, pCreateInfo, pAllocator, pSurface); +} +*/ +import "C" +import ( + "fmt" + "unsafe" +) + +var wlFuncs struct { + vkCreateAndroidSurfaceKHR C.PFN_vkCreateAndroidSurfaceKHR +} + +func init() { + loadFuncs = append(loadFuncs, func(dlopen func(name string) *[0]byte) { + wlFuncs.vkCreateAndroidSurfaceKHR = dlopen("vkCreateAndroidSurfaceKHR") + }) +} + +func CreateAndroidSurface(inst Instance, window unsafe.Pointer) (Surface, error) { + inf := C.VkAndroidSurfaceCreateInfoKHR{ + sType: C.VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR, + window: (*C.ANativeWindow)(window), + } + var surf Surface + if err := vkErr(C.vkCreateAndroidSurfaceKHR(wlFuncs.vkCreateAndroidSurfaceKHR, inst, &inf, nil, &surf)); err != nil { + return 0, fmt.Errorf("vulkan: vkCreateAndroidSurfaceKHR: %w", err) + } + return surf, nil +} diff --git a/vendor/gioui.org/internal/vk/vulkan_wayland.go b/vendor/gioui.org/internal/vk/vulkan_wayland.go new file mode 100644 index 0000000..cb057bc --- /dev/null +++ b/vendor/gioui.org/internal/vk/vulkan_wayland.go @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build ((linux && !android) || freebsd) && !nowayland +// +build linux,!android freebsd +// +build !nowayland + +package vk + +/* +#define VK_USE_PLATFORM_WAYLAND_KHR +#define VK_NO_PROTOTYPES 1 +#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object; +#include <vulkan/vulkan.h> + +static VkResult vkCreateWaylandSurfaceKHR(PFN_vkCreateWaylandSurfaceKHR f, VkInstance instance, const VkWaylandSurfaceCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) { + return f(instance, pCreateInfo, pAllocator, pSurface); +} +*/ +import "C" +import ( + "fmt" + "unsafe" +) + +var wlFuncs struct { + vkCreateWaylandSurfaceKHR C.PFN_vkCreateWaylandSurfaceKHR +} + +func init() { + loadFuncs = append(loadFuncs, func(dlopen func(name string) *[0]byte) { + wlFuncs.vkCreateWaylandSurfaceKHR = dlopen("vkCreateWaylandSurfaceKHR") + }) +} + +func CreateWaylandSurface(inst Instance, disp unsafe.Pointer, wlSurf unsafe.Pointer) (Surface, error) { + inf := C.VkWaylandSurfaceCreateInfoKHR{ + sType: C.VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, + display: (*C.struct_wl_display)(disp), + surface: (*C.struct_wl_surface)(wlSurf), + } + var surf Surface + if err := vkErr(C.vkCreateWaylandSurfaceKHR(wlFuncs.vkCreateWaylandSurfaceKHR, inst, &inf, nil, &surf)); err != nil { + return 0, fmt.Errorf("vulkan: vkCreateWaylandSurfaceKHR: %w", err) + } + return surf, nil +} diff --git a/vendor/gioui.org/internal/vk/vulkan_x11.go b/vendor/gioui.org/internal/vk/vulkan_x11.go new file mode 100644 index 0000000..780a5d5 --- /dev/null +++ b/vendor/gioui.org/internal/vk/vulkan_x11.go @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build ((linux && !android) || freebsd) && !nox11 +// +build linux,!android freebsd +// +build !nox11 + +package vk + +/* +#define VK_USE_PLATFORM_XLIB_KHR +#define VK_NO_PROTOTYPES 1 +#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object; +#include <vulkan/vulkan.h> + +static VkResult vkCreateXlibSurfaceKHR(PFN_vkCreateXlibSurfaceKHR f, VkInstance instance, const VkXlibSurfaceCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface) { + return f(instance, pCreateInfo, pAllocator, pSurface); +} +*/ +import "C" +import ( + "fmt" + "unsafe" +) + +var x11Funcs struct { + vkCreateXlibSurfaceKHR C.PFN_vkCreateXlibSurfaceKHR +} + +func init() { + loadFuncs = append(loadFuncs, func(dlopen func(name string) *[0]byte) { + x11Funcs.vkCreateXlibSurfaceKHR = dlopen("vkCreateXlibSurfaceKHR") + }) +} + +func CreateXlibSurface(inst Instance, dpy unsafe.Pointer, window uintptr) (Surface, error) { + inf := C.VkXlibSurfaceCreateInfoKHR{ + sType: C.VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, + dpy: (*C.Display)(dpy), + window: (C.Window)(window), + } + var surf Surface + if err := vkErr(C.vkCreateXlibSurfaceKHR(x11Funcs.vkCreateXlibSurfaceKHR, inst, &inf, nil, &surf)); err != nil { + return 0, fmt.Errorf("vulkan: vkCreateXlibSurfaceKHR: %w", err) + } + return surf, nil +} diff --git a/vendor/gioui.org/io/clipboard/clipboard.go b/vendor/gioui.org/io/clipboard/clipboard.go new file mode 100644 index 0000000..ae4a435 --- /dev/null +++ b/vendor/gioui.org/io/clipboard/clipboard.go @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package clipboard + +import ( + "gioui.org/internal/ops" + "gioui.org/io/event" + "gioui.org/op" +) + +// Event is generated when the clipboard content is requested. +type Event struct { + Text string +} + +// ReadOp requests the text of the clipboard, delivered to +// the current handler through an Event. +type ReadOp struct { + Tag event.Tag +} + +// WriteOp copies Text to the clipboard. +type WriteOp struct { + Text string +} + +func (h ReadOp) Add(o *op.Ops) { + data := ops.Write1(&o.Internal, ops.TypeClipboardReadLen, h.Tag) + data[0] = byte(ops.TypeClipboardRead) +} + +func (h WriteOp) Add(o *op.Ops) { + data := ops.Write1(&o.Internal, ops.TypeClipboardWriteLen, &h.Text) + data[0] = byte(ops.TypeClipboardWrite) +} + +func (Event) ImplementsEvent() {} diff --git a/vendor/gioui.org/io/key/key.go b/vendor/gioui.org/io/key/key.go index 5729ae4..b6af2a4 100644 --- a/vendor/gioui.org/io/key/key.go +++ b/vendor/gioui.org/io/key/key.go @@ -10,25 +10,35 @@ events. package key import ( + "fmt" "strings" - "gioui.org/internal/opconst" + "gioui.org/internal/ops" "gioui.org/io/event" "gioui.org/op" ) // InputOp declares a handler ready for key events. // Key events are in general only delivered to the -// focused key handler. Set the Focus flag to request -// the focus. +// focused key handler. type InputOp struct { - Tag event.Tag - Focus bool + Tag event.Tag + Hint InputHint } -// HideInputOp request that any on screen text input -// be hidden. -type HideInputOp struct{} +// SoftKeyboardOp shows or hide the on-screen keyboard, if available. +// It replaces any previous SoftKeyboardOp. +type SoftKeyboardOp struct { + Show bool +} + +// FocusOp sets or clears the keyboard focus. It replaces any previous +// FocusOp in the same frame. +type FocusOp struct { + // Tag is the new focus. The focus is cleared if Tag is nil, or if Tag + // has no InputOp in the same frame. + Tag event.Tag +} // A FocusEvent is generated when a handler gains or loses // focus. @@ -46,6 +56,8 @@ type Event struct { Name string // Modifiers is the set of active modifiers when the key was pressed. Modifiers Modifiers + // State is the state of the key when the event was fired. + State State } // An EditEvent is generated when text is input. @@ -53,6 +65,38 @@ type EditEvent struct { Text string } +// InputHint changes the on-screen-keyboard type. That hints the +// type of data that might be entered by the user. +type InputHint uint8 + +const ( + // HintAny hints that any input is expected. + HintAny InputHint = iota + // HintText hints that text input is expected. It may activate auto-correction and suggestions. + HintText + // HintNumeric hints that numeric input is expected. It may activate shortcuts for 0-9, "." and ",". + HintNumeric + // HintEmail hints that email input is expected. It may activate shortcuts for common email characters, such as "@" and ".com". + HintEmail + // HintURL hints that URL input is expected. It may activate shortcuts for common URL fragments such as "/" and ".com". + HintURL + // HintTelephone hints that telephone number input is expected. It may activate shortcuts for 0-9, "#" and "*". + HintTelephone +) + +// State is the state of a key during an event. +type State uint8 + +const ( + // Press is the state of a pressed key. + Press State = iota + // Release is the state of a key that has been released. + // + // Note: release events are only implemented on the following platforms: + // macOS, Linux, Windows, WebAssembly. + Release +) + // Modifiers type Modifiers uint32 @@ -88,6 +132,7 @@ const ( NamePageUp = "⇞" NamePageDown = "⇟" NameTab = "⇥" + NameSpace = "Space" ) // Contain reports whether m contains all modifiers @@ -97,16 +142,25 @@ func (m Modifiers) Contain(m2 Modifiers) bool { } func (h InputOp) Add(o *op.Ops) { - data := o.Write(opconst.TypeKeyInputLen, h.Tag) - data[0] = byte(opconst.TypeKeyInput) - if h.Focus { + if h.Tag == nil { + panic("Tag must be non-nil") + } + data := ops.Write1(&o.Internal, ops.TypeKeyInputLen, h.Tag) + data[0] = byte(ops.TypeKeyInput) + data[1] = byte(h.Hint) +} + +func (h SoftKeyboardOp) Add(o *op.Ops) { + data := ops.Write(&o.Internal, ops.TypeKeySoftKeyboardLen) + data[0] = byte(ops.TypeKeySoftKeyboard) + if h.Show { data[1] = 1 } } -func (h HideInputOp) Add(o *op.Ops) { - data := o.Write(opconst.TypeHideInputLen) - data[0] = byte(opconst.TypeHideInput) +func (h FocusOp) Add(o *op.Ops) { + data := ops.Write1(&o.Internal, ops.TypeKeyFocusLen, h.Tag) + data[0] = byte(ops.TypeKeyFocus) } func (EditEvent) ImplementsEvent() {} @@ -114,25 +168,36 @@ func (Event) ImplementsEvent() {} func (FocusEvent) ImplementsEvent() {} func (e Event) String() string { - return "{" + string(e.Name) + " " + e.Modifiers.String() + "}" + return fmt.Sprintf("%v %v %v}", e.Name, e.Modifiers, e.State) } func (m Modifiers) String() string { var strs []string if m.Contain(ModCtrl) { - strs = append(strs, "ModCtrl") + strs = append(strs, "Ctrl") } if m.Contain(ModCommand) { - strs = append(strs, "ModCommand") + strs = append(strs, "Command") } if m.Contain(ModShift) { - strs = append(strs, "ModShift") + strs = append(strs, "Shift") } if m.Contain(ModAlt) { - strs = append(strs, "ModAlt") + strs = append(strs, "Alt") } if m.Contain(ModSuper) { - strs = append(strs, "ModSuper") + strs = append(strs, "Super") } return strings.Join(strs, "|") } + +func (s State) String() string { + switch s { + case Press: + return "Press" + case Release: + return "Release" + default: + panic("invalid State") + } +} diff --git a/vendor/gioui.org/io/key/mod.go b/vendor/gioui.org/io/key/mod.go index c5db56c..4b23d32 100644 --- a/vendor/gioui.org/io/key/mod.go +++ b/vendor/gioui.org/io/key/mod.go @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Unlicense OR MIT +//go:build !darwin // +build !darwin package key diff --git a/vendor/gioui.org/io/pointer/doc.go b/vendor/gioui.org/io/pointer/doc.go index b99d97d..2f521c2 100644 --- a/vendor/gioui.org/io/pointer/doc.go +++ b/vendor/gioui.org/io/pointer/doc.go @@ -25,63 +25,67 @@ Leave, or Scroll): Cancel events are always delivered. -Areas +Hit areas -The area operations are used for specifying the area where -subsequent InputOp are active. +Clip operations from package op/clip are used for specifying +hit areas where subsequent InputOps are active. -For example, to set up a rectangular hit area: +For example, to set up a handler with a rectangular hit area: r := image.Rectangle{...} - pointer.Rect(r).Add(ops) + area := clip.Rect(r).Push(ops) pointer.InputOp{Tag: h}.Add(ops) + area.Pop() -Note that areas compound: the effective area of multiple area -operations is the intersection of the areas. +Note that hit areas behave similar to painting: the effective area of a stack +of multiple area operations is the intersection of the areas. + +BUG: Clip operations other than clip.Rect and clip.Ellipse are approximated +with their bounding boxes. Matching events -StackOp operations and input handlers form an implicit tree. -Each stack operation is a node, and each input handler is associated -with the most recent node. +Areas form an implicit tree, with input handlers as leaves. The children of +an area is every area and handler added between its Push and corresponding Pop. For example: ops := new(op.Ops) - var stack op.StackOp var h1, h2 *Handler - stack := op.Push(ops) + area := clip.Rect(...).Push(ops) pointer.InputOp{Tag: h1}.Add(Ops) - stack.Pop() + area.Pop() - stack = op.Push(ops) + area := clip.Rect(...).Push(ops) pointer.InputOp{Tag: h2}.Add(ops) - stack.Pop() + area.Pop() + +implies a tree of two inner nodes, each with one pointer handler attached. -implies a tree of two inner nodes, each with one pointer handler. +The matching proceeds as follows. -When determining which handlers match an Event, only handlers whose -areas contain the event position are considered. The matching -proceeds as follows. +First, the foremost area that contains the event is found. Only areas whose +parent areas all contain the event is considered. -First, the foremost matching handler is included. If the handler -has pass-through enabled, this step is repeated. +Then, every handler attached to the area is matched with the event. -Then, all matching handlers from the current node and all parent -nodes are included. +If all attached handlers are marked pass-through or if no handlers are +attached, the matching repeats with the next foremost (sibling) area. Otherwise +the matching repeats with the parent area. -In the example above, all events will go to h2 only even though both -handlers have the same area (the entire screen). +In the example above, all events will go to h2 because it and h1 are siblings +and none are pass-through. Pass-through -The PassOp operations controls the pass-through setting. A handler's -pass-through setting is recorded along with the InputOp. +The PassOp operations controls the pass-through setting. All handlers added +inside one or more PassOp scopes are marked pass-through. -Pass-through handlers are useful for overlay widgets such as a hidden -side drawer. When the user touches the side, both the (transparent) -drawer handle and the interface below should receive pointer events. +Pass-through is useful for overlay widgets. Consider a hidden side drawer: when +the user touches the side, both the (transparent) drawer handle and the +interface below should receive pointer events. This effect is achieved by +marking the drawer handle pass-through. Disambiguation diff --git a/vendor/gioui.org/io/pointer/pointer.go b/vendor/gioui.org/io/pointer/pointer.go index 3344d6b..97987f7 100644 --- a/vendor/gioui.org/io/pointer/pointer.go +++ b/vendor/gioui.org/io/pointer/pointer.go @@ -4,12 +4,13 @@ package pointer import ( "encoding/binary" + "fmt" "image" "strings" "time" "gioui.org/f32" - "gioui.org/internal/opconst" + "gioui.org/internal/ops" "gioui.org/io/event" "gioui.org/io/key" "gioui.org/op" @@ -41,12 +42,21 @@ type Event struct { Modifiers key.Modifiers } -// AreaOp updates the hit area to the intersection of the current -// hit area and the area. The area is transformed before applying -// it. -type AreaOp struct { - kind areaKind - rect image.Rectangle +// PassOp sets the pass-through mode. InputOps added while the pass-through +// mode is set don't block events to siblings. +type PassOp struct { +} + +// PassStack represents a PassOp on the pass stack. +type PassStack struct { + ops *ops.Ops + id ops.StackID + macroID int +} + +// CursorNameOp sets the cursor for the current area. +type CursorNameOp struct { + Name CursorName } // InputOp declares an input handler ready for pointer @@ -58,17 +68,18 @@ type InputOp struct { Grab bool // Types is a bitwise-or of event types to receive. Types Type -} - -// PassOp sets the pass-through mode. -type PassOp struct { - Pass bool + // ScrollBounds describe the maximum scrollable distances in both + // axes. Specifically, any Event e delivered to Tag will satisfy + // + // ScrollBounds.Min.X <= e.Scroll.X <= ScrollBounds.Max.X (horizontal axis) + // ScrollBounds.Min.Y <= e.Scroll.Y <= ScrollBounds.Max.Y (vertical axis) + ScrollBounds image.Rectangle } type ID uint16 // Type of an Event. -type Type uint8 +type Type uint // Priority of an Event. type Priority uint8 @@ -79,8 +90,27 @@ type Source uint8 // Buttons is a set of mouse buttons type Buttons uint8 -// Must match app/internal/input.areaKind -type areaKind uint8 +// CursorName is the name of a cursor. +type CursorName string + +const ( + // CursorDefault is the default cursor. + CursorDefault CursorName = "" + // CursorText is the cursor for text. + CursorText CursorName = "text" + // CursorPointer is the cursor for a link. + CursorPointer CursorName = "pointer" + // CursorCrossHair is the cursor for precise location. + CursorCrossHair CursorName = "crosshair" + // CursorColResize is the cursor for vertical resize. + CursorColResize CursorName = "col-resize" + // CursorRowResize is the cursor for horizontal resize. + CursorRowResize CursorName = "row-resize" + // CursorGrab is the cursor for moving object in any direction. + CursorGrab CursorName = "grab" + // CursorNone hides the cursor. To show it again, use any other cursor. + CursorNone CursorName = "none" +) const ( // A Cancel event is generated when the current gesture is @@ -121,61 +151,90 @@ const ( ) const ( - ButtonLeft Buttons = 1 << iota - ButtonRight - ButtonMiddle -) - -const ( - areaRect areaKind = iota - areaEllipse + // ButtonPrimary is the primary button, usually the left button for a + // right-handed user. + ButtonPrimary Buttons = 1 << iota + // ButtonSecondary is the secondary button, usually the right button for a + // right-handed user. + ButtonSecondary + // ButtonTertiary is the tertiary button, usually the middle button. + ButtonTertiary ) -// Rect constructs a rectangular hit area. -func Rect(size image.Rectangle) AreaOp { - return AreaOp{ - kind: areaRect, - rect: size, +// frect converts a rectangle to a f32.Rectangle. +func frect(r image.Rectangle) f32.Rectangle { + return f32.Rectangle{ + Min: fpt(r.Min), Max: fpt(r.Max), } } -// Ellipse constructs an ellipsoid hit area. -func Ellipse(size image.Rectangle) AreaOp { - return AreaOp{ - kind: areaEllipse, - rect: size, +// fpt converts an point to a f32.Point. +func fpt(p image.Point) f32.Point { + return f32.Point{ + X: float32(p.X), Y: float32(p.Y), } } -func (op AreaOp) Add(o *op.Ops) { - data := o.Write(opconst.TypeAreaLen) - data[0] = byte(opconst.TypeArea) - data[1] = byte(op.kind) - bo := binary.LittleEndian - bo.PutUint32(data[2:], uint32(op.rect.Min.X)) - bo.PutUint32(data[6:], uint32(op.rect.Min.Y)) - bo.PutUint32(data[10:], uint32(op.rect.Max.X)) - bo.PutUint32(data[14:], uint32(op.rect.Max.Y)) +// Push the current pass mode to the pass stack and set the pass mode. +func (p PassOp) Push(o *op.Ops) PassStack { + id, mid := ops.PushOp(&o.Internal, ops.PassStack) + data := ops.Write(&o.Internal, ops.TypePassLen) + data[0] = byte(ops.TypePass) + return PassStack{ops: &o.Internal, id: id, macroID: mid} } -func (h InputOp) Add(o *op.Ops) { - data := o.Write(opconst.TypePointerInputLen, h.Tag) - data[0] = byte(opconst.TypePointerInput) - if h.Grab { - data[1] = 1 - } - data[2] = byte(h.Types) +func (p PassStack) Pop() { + ops.PopOp(p.ops, ops.PassStack, p.id, p.macroID) + data := ops.Write(p.ops, ops.TypePopPassLen) + data[0] = byte(ops.TypePopPass) +} + +func (op CursorNameOp) Add(o *op.Ops) { + data := ops.Write1(&o.Internal, ops.TypeCursorLen, op.Name) + data[0] = byte(ops.TypeCursor) } -func (op PassOp) Add(o *op.Ops) { - data := o.Write(opconst.TypePassLen) - data[0] = byte(opconst.TypePass) - if op.Pass { +// Add panics if the scroll range does not contain zero. +func (op InputOp) Add(o *op.Ops) { + if op.Tag == nil { + panic("Tag must be non-nil") + } + if b := op.ScrollBounds; b.Min.X > 0 || b.Max.X < 0 || b.Min.Y > 0 || b.Max.Y < 0 { + panic(fmt.Errorf("invalid scroll range value %v", b)) + } + if op.Types>>16 > 0 { + panic(fmt.Errorf("value in Types overflows uint16")) + } + data := ops.Write1(&o.Internal, ops.TypePointerInputLen, op.Tag) + data[0] = byte(ops.TypePointerInput) + if op.Grab { data[1] = 1 } + bo := binary.LittleEndian + bo.PutUint16(data[2:], uint16(op.Types)) + bo.PutUint32(data[4:], uint32(op.ScrollBounds.Min.X)) + bo.PutUint32(data[8:], uint32(op.ScrollBounds.Min.Y)) + bo.PutUint32(data[12:], uint32(op.ScrollBounds.Max.X)) + bo.PutUint32(data[16:], uint32(op.ScrollBounds.Max.Y)) } func (t Type) String() string { + if t == Cancel { + return "Cancel" + } + var buf strings.Builder + for tt := Type(1); tt > 0; tt <<= 1 { + if t&tt > 0 { + if buf.Len() > 0 { + buf.WriteByte('|') + } + buf.WriteString((t & tt).string()) + } + } + return buf.String() +} + +func (t Type) string() string { switch t { case Press: return "Press" @@ -230,16 +289,23 @@ func (b Buttons) Contain(buttons Buttons) bool { func (b Buttons) String() string { var strs []string - if b.Contain(ButtonLeft) { - strs = append(strs, "ButtonLeft") + if b.Contain(ButtonPrimary) { + strs = append(strs, "ButtonPrimary") } - if b.Contain(ButtonRight) { - strs = append(strs, "ButtonRight") + if b.Contain(ButtonSecondary) { + strs = append(strs, "ButtonSecondary") } - if b.Contain(ButtonMiddle) { - strs = append(strs, "ButtonMiddle") + if b.Contain(ButtonTertiary) { + strs = append(strs, "ButtonTertiary") } return strings.Join(strs, "|") } +func (c CursorName) String() string { + if c == CursorDefault { + return "default" + } + return string(c) +} + func (Event) ImplementsEvent() {} diff --git a/vendor/gioui.org/io/profile/profile.go b/vendor/gioui.org/io/profile/profile.go index 24bf52b..b9a4476 100644 --- a/vendor/gioui.org/io/profile/profile.go +++ b/vendor/gioui.org/io/profile/profile.go @@ -5,7 +5,7 @@ package profile import ( - "gioui.org/internal/opconst" + "gioui.org/internal/ops" "gioui.org/io/event" "gioui.org/op" ) @@ -24,8 +24,8 @@ type Event struct { } func (p Op) Add(o *op.Ops) { - data := o.Write(opconst.TypeProfileLen, p.Tag) - data[0] = byte(opconst.TypeProfile) + data := ops.Write1(&o.Internal, ops.TypeProfileLen, p.Tag) + data[0] = byte(ops.TypeProfile) } func (p Event) ImplementsEvent() {} diff --git a/vendor/gioui.org/io/router/clipboard.go b/vendor/gioui.org/io/router/clipboard.go new file mode 100644 index 0000000..5f1623c --- /dev/null +++ b/vendor/gioui.org/io/router/clipboard.go @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package router + +import ( + "gioui.org/io/event" +) + +type clipboardQueue struct { + receivers map[event.Tag]struct{} + // request avoid read clipboard every frame while waiting. + requested bool + text *string +} + +// WriteClipboard returns the most recent text to be copied +// to the clipboard, if any. +func (q *clipboardQueue) WriteClipboard() (string, bool) { + if q.text == nil { + return "", false + } + text := *q.text + q.text = nil + return text, true +} + +// ReadClipboard reports if any new handler is waiting +// to read the clipboard. +func (q *clipboardQueue) ReadClipboard() bool { + if len(q.receivers) == 0 || q.requested { + return false + } + q.requested = true + return true +} + +func (q *clipboardQueue) Push(e event.Event, events *handlerEvents) { + for r := range q.receivers { + events.Add(r, e) + delete(q.receivers, r) + } +} + +func (q *clipboardQueue) ProcessWriteClipboard(refs []interface{}) { + q.text = refs[0].(*string) +} + +func (q *clipboardQueue) ProcessReadClipboard(refs []interface{}) { + if q.receivers == nil { + q.receivers = make(map[event.Tag]struct{}) + } + tag := refs[0].(event.Tag) + if _, ok := q.receivers[tag]; !ok { + q.receivers[tag] = struct{}{} + q.requested = false + } +} diff --git a/vendor/gioui.org/io/router/key.go b/vendor/gioui.org/io/router/key.go index a64544b..9fef7dc 100644 --- a/vendor/gioui.org/io/router/key.go +++ b/vendor/gioui.org/io/router/key.go @@ -3,11 +3,8 @@ package router import ( - "gioui.org/internal/opconst" - "gioui.org/internal/ops" "gioui.org/io/event" "gioui.org/io/key" - "gioui.org/op" ) type TextInputState uint8 @@ -15,22 +12,25 @@ type TextInputState uint8 type keyQueue struct { focus event.Tag handlers map[event.Tag]*keyHandler - reader ops.Reader state TextInputState + hint key.InputHint } type keyHandler struct { - active bool + // visible will be true if the InputOp is present + // in the current frame. + visible bool + new bool + hint key.InputHint } -type listenerPriority uint8 - -const ( - priNone listenerPriority = iota - priDefault - priCurrentFocus - priNewFocus -) +// keyCollector tracks state required to update a keyQueue +// from key ops. +type keyCollector struct { + q *keyQueue + focus event.Tag + changed bool +} const ( TextInputKeep TextInputState = iota @@ -44,43 +44,60 @@ func (q *keyQueue) InputState() TextInputState { return q.state } -func (q *keyQueue) Frame(root *op.Ops, events *handlerEvents) { +// InputHint returns the input mode from the most recent key.InputOp. +func (q *keyQueue) InputHint() (key.InputHint, bool) { + if q.focus == nil { + return q.hint, false + } + focused, ok := q.handlers[q.focus] + if !ok { + return q.hint, false + } + old := q.hint + q.hint = focused.hint + return q.hint, old != q.hint +} + +func (q *keyQueue) Reset() { if q.handlers == nil { q.handlers = make(map[event.Tag]*keyHandler) } for _, h := range q.handlers { - h.active = false + h.visible, h.new = false, false } - q.reader.Reset(root) - focus, pri, hide := q.resolveFocus(events) + q.state = TextInputKeep +} + +func (q *keyQueue) Frame(events *handlerEvents, collector keyCollector) { for k, h := range q.handlers { - if !h.active { + if !h.visible { delete(q.handlers, k) if q.focus == k { + // Remove the focus from the handler that is no longer visible. q.focus = nil - hide = true + q.state = TextInputClose } + } else if h.new && k != collector.focus { + // Reset the handler on (each) first appearance, but don't trigger redraw. + events.AddNoRedraw(k, key.FocusEvent{Focus: false}) + } + } + if collector.changed && collector.focus != nil { + if _, exists := q.handlers[collector.focus]; !exists { + collector.focus = nil } } - if focus != q.focus { + if collector.changed && collector.focus != q.focus { if q.focus != nil { events.Add(q.focus, key.FocusEvent{Focus: false}) } - q.focus = focus + q.focus = collector.focus if q.focus != nil { events.Add(q.focus, key.FocusEvent{Focus: true}) } else { - hide = true + q.state = TextInputClose } } - switch { - case pri == priNewFocus: - q.state = TextInputOpen - case hide: - q.state = TextInputClose - default: - q.state = TextInputKeep - } } func (q *keyQueue) Push(e event.Event, events *handlerEvents) { @@ -89,62 +106,38 @@ func (q *keyQueue) Push(e event.Event, events *handlerEvents) { } } -func (q *keyQueue) resolveFocus(events *handlerEvents) (event.Tag, listenerPriority, bool) { - var k event.Tag - var pri listenerPriority - var hide bool -loop: - for encOp, ok := q.reader.Decode(); ok; encOp, ok = q.reader.Decode() { - switch opconst.OpType(encOp.Data[0]) { - case opconst.TypeKeyInput: - op := decodeKeyInputOp(encOp.Data, encOp.Refs) - var newPri listenerPriority - switch { - case op.Focus: - newPri = priNewFocus - case op.Tag == q.focus: - newPri = priCurrentFocus - default: - newPri = priDefault - } - // Switch focus if higher priority or if focus requested. - if newPri.replaces(pri) { - k, pri = op.Tag, newPri - } - h, ok := q.handlers[op.Tag] - if !ok { - h = new(keyHandler) - q.handlers[op.Tag] = h - // Reset the handler on (each) first appearance. - events.Add(op.Tag, key.FocusEvent{Focus: false}) - } - h.active = true - case opconst.TypeHideInput: - hide = true - case opconst.TypePush: - newK, newPri, h := q.resolveFocus(events) - hide = hide || h - if newPri.replaces(pri) { - k, pri = newK, newPri - } - case opconst.TypePop: - break loop - } - } - return k, pri, hide +func (k *keyCollector) focusOp(tag event.Tag) { + k.focus = tag + k.changed = true } -func (p listenerPriority) replaces(p2 listenerPriority) bool { - // Favor earliest default focus or latest requested focus. - return p > p2 || p == p2 && p == priNewFocus +func (k *keyCollector) softKeyboard(show bool) { + if show { + k.q.state = TextInputOpen + } else { + k.q.state = TextInputClose + } } -func decodeKeyInputOp(d []byte, refs []interface{}) key.InputOp { - if opconst.OpType(d[0]) != opconst.TypeKeyInput { - panic("invalid op") +func (k *keyCollector) inputOp(op key.InputOp) { + h, ok := k.q.handlers[op.Tag] + if !ok { + h = &keyHandler{new: true} + k.q.handlers[op.Tag] = h } - return key.InputOp{ - Tag: refs[0].(event.Tag), - Focus: d[1] != 0, + h.visible = true + h.hint = op.Hint +} + +func (t TextInputState) String() string { + switch t { + case TextInputKeep: + return "Keep" + case TextInputClose: + return "Close" + case TextInputOpen: + return "Open" + default: + panic("unexpected value") } } diff --git a/vendor/gioui.org/io/router/pointer.go b/vendor/gioui.org/io/router/pointer.go index 0d0977f..cb30f63 100644 --- a/vendor/gioui.org/io/router/pointer.go +++ b/vendor/gioui.org/io/router/pointer.go @@ -3,44 +3,65 @@ package router import ( - "encoding/binary" "image" + "io" "gioui.org/f32" - "gioui.org/internal/opconst" "gioui.org/internal/ops" "gioui.org/io/event" "gioui.org/io/pointer" - "gioui.org/op" + "gioui.org/io/semantic" + "gioui.org/io/transfer" ) type pointerQueue struct { - hitTree []hitNode - areas []areaNode - handlers map[event.Tag]*pointerHandler - pointers []pointerInfo - reader ops.Reader + hitTree []hitNode + areas []areaNode + cursors []cursorNode + cursor pointer.CursorName + handlers map[event.Tag]*pointerHandler + pointers []pointerInfo + transfers []io.ReadCloser // pending data transfers scratch []event.Tag + + semantic struct { + idsAssigned bool + lastID SemanticID + // contentIDs maps semantic content to a list of semantic IDs + // previously assigned. It is used to maintain stable IDs across + // frames. + contentIDs map[semanticContent][]semanticID + } } type hitNode struct { next int area int - // Pass tracks the most recent PassOp mode. - pass bool // For handler nodes. - tag event.Tag + tag event.Tag + pass bool +} + +type cursorNode struct { + name pointer.CursorName + area int } type pointerInfo struct { id pointer.ID pressed bool handlers []event.Tag + // last tracks the last pointer event received, + // used while processing frame events. + last pointer.Event // entered tracks the tags that contain the pointer. entered []event.Tag + + dataSource event.Tag // dragging source tag + dataTarget event.Tag // dragging target tag } type pointerHandler struct { @@ -48,80 +69,381 @@ type pointerHandler struct { active bool wantsGrab bool types pointer.Type + // min and max horizontal/vertical scroll + scrollRange image.Rectangle + + sourceMimes []string + targetMimes []string + offeredMime string + data io.ReadCloser } type areaOp struct { kind areaKind - rect image.Rectangle + rect f32.Rectangle } type areaNode struct { trans f32.Affine2D - next int area areaOp + + // Tree indices, with -1 being the sentinel. + parent int + firstChild int + lastChild int + sibling int + + semantic struct { + valid bool + id SemanticID + content semanticContent + } } type areaKind uint8 +// collectState represents the state for pointerCollector. +type collectState struct { + t f32.Affine2D + // nodePlusOne is the current node index, plus one to + // make the zero value collectState the initial state. + nodePlusOne int + pass int +} + +// pointerCollector tracks the state needed to update an pointerQueue +// from pointer ops. +type pointerCollector struct { + q *pointerQueue + state collectState + nodeStack []int +} + +type semanticContent struct { + tag event.Tag + label string + desc string + class semantic.ClassOp + gestures SemanticGestures + selected bool + disabled bool +} + +type semanticID struct { + id SemanticID + used bool +} + const ( areaRect areaKind = iota areaEllipse ) -func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents, t f32.Affine2D, area, node int, pass bool) { - for encOp, ok := r.Decode(); ok; encOp, ok = r.Decode() { - switch opconst.OpType(encOp.Data[0]) { - case opconst.TypePush: - q.collectHandlers(r, events, t, area, node, pass) - case opconst.TypePop: - return - case opconst.TypePass: - op := decodePassOp(encOp.Data) - pass = op.Pass - case opconst.TypeArea: - var op areaOp - op.Decode(encOp.Data) - q.areas = append(q.areas, areaNode{trans: t, next: area, area: op}) - area = len(q.areas) - 1 - q.hitTree = append(q.hitTree, hitNode{ - next: node, - area: area, - pass: pass, - }) - node = len(q.hitTree) - 1 - case opconst.TypeTransform: - dop := ops.DecodeTransform(encOp.Data) - t = t.Mul(dop) - case opconst.TypePointerInput: - op := decodePointerInputOp(encOp.Data, encOp.Refs) - q.hitTree = append(q.hitTree, hitNode{ - next: node, - area: area, - pass: pass, - tag: op.Tag, - }) - node = len(q.hitTree) - 1 - h, ok := q.handlers[op.Tag] - if !ok { - h = new(pointerHandler) - q.handlers[op.Tag] = h - events.Add(op.Tag, pointer.Event{Type: pointer.Cancel}) - } - h.active = true - h.area = area - h.wantsGrab = h.wantsGrab || op.Grab - h.types = h.types | op.Types +func (c *pointerCollector) resetState() { + c.state = collectState{} +} + +func (c *pointerCollector) setTrans(t f32.Affine2D) { + c.state.t = t +} + +func (c *pointerCollector) clip(op ops.ClipOp) { + kind := areaRect + if op.Shape == ops.Ellipse { + kind = areaEllipse + } + c.pushArea(kind, frect(op.Bounds)) +} + +func (c *pointerCollector) pushArea(kind areaKind, bounds f32.Rectangle) { + parentID := c.currentArea() + areaID := len(c.q.areas) + areaOp := areaOp{kind: kind, rect: bounds} + if parentID != -1 { + parent := &c.q.areas[parentID] + if parent.firstChild == -1 { + parent.firstChild = areaID + } + if siblingID := parent.lastChild; siblingID != -1 { + c.q.areas[siblingID].sibling = areaID + } + parent.lastChild = areaID + } + an := areaNode{ + trans: c.state.t, + area: areaOp, + parent: parentID, + sibling: -1, + firstChild: -1, + lastChild: -1, + } + + c.q.areas = append(c.q.areas, an) + c.nodeStack = append(c.nodeStack, c.state.nodePlusOne-1) + c.addHitNode(hitNode{ + area: areaID, + pass: true, + }) +} + +// frect converts a rectangle to a f32.Rectangle. +func frect(r image.Rectangle) f32.Rectangle { + return f32.Rectangle{ + Min: fpt(r.Min), Max: fpt(r.Max), + } +} + +// fpt converts a point to a f32.Point. +func fpt(p image.Point) f32.Point { + return f32.Point{ + X: float32(p.X), Y: float32(p.Y), + } +} + +func (c *pointerCollector) popArea() { + n := len(c.nodeStack) + c.state.nodePlusOne = c.nodeStack[n-1] + 1 + c.nodeStack = c.nodeStack[:n-1] +} + +func (c *pointerCollector) pass() { + c.state.pass++ +} + +func (c *pointerCollector) popPass() { + c.state.pass-- +} + +func (c *pointerCollector) currentArea() int { + if i := c.state.nodePlusOne - 1; i != -1 { + n := c.q.hitTree[i] + return n.area + } + return -1 +} + +func (c *pointerCollector) addHitNode(n hitNode) { + n.next = c.state.nodePlusOne - 1 + c.q.hitTree = append(c.q.hitTree, n) + c.state.nodePlusOne = len(c.q.hitTree) - 1 + 1 +} + +// newHandler returns the current handler or a new one for tag. +func (c *pointerCollector) newHandler(tag event.Tag, events *handlerEvents) *pointerHandler { + areaID := c.currentArea() + c.addHitNode(hitNode{ + area: areaID, + tag: tag, + pass: c.state.pass > 0, + }) + h, ok := c.q.handlers[tag] + if !ok { + h = new(pointerHandler) + c.q.handlers[tag] = h + // Cancel handlers on (each) first appearance, but don't + // trigger redraw. + events.AddNoRedraw(tag, pointer.Event{Type: pointer.Cancel}) + } + h.active = true + h.area = areaID + return h +} + +func (c *pointerCollector) inputOp(op pointer.InputOp, events *handlerEvents) { + areaID := c.currentArea() + area := &c.q.areas[areaID] + area.semantic.content.tag = op.Tag + if op.Types&(pointer.Press|pointer.Release) != 0 { + area.semantic.content.gestures |= ClickGesture + } + area.semantic.valid = area.semantic.content.gestures != 0 + h := c.newHandler(op.Tag, events) + h.wantsGrab = h.wantsGrab || op.Grab + h.types = h.types | op.Types + h.scrollRange = op.ScrollBounds +} + +func (c *pointerCollector) semanticLabel(lbl string) { + areaID := c.currentArea() + area := &c.q.areas[areaID] + area.semantic.valid = true + area.semantic.content.label = lbl +} + +func (c *pointerCollector) semanticDesc(desc string) { + areaID := c.currentArea() + area := &c.q.areas[areaID] + area.semantic.valid = true + area.semantic.content.desc = desc +} + +func (c *pointerCollector) semanticClass(class semantic.ClassOp) { + areaID := c.currentArea() + area := &c.q.areas[areaID] + area.semantic.valid = true + area.semantic.content.class = class +} + +func (c *pointerCollector) semanticSelected(selected bool) { + areaID := c.currentArea() + area := &c.q.areas[areaID] + area.semantic.valid = true + area.semantic.content.selected = selected +} + +func (c *pointerCollector) semanticDisabled(disabled bool) { + areaID := c.currentArea() + area := &c.q.areas[areaID] + area.semantic.valid = true + area.semantic.content.disabled = disabled +} + +func (c *pointerCollector) cursor(name pointer.CursorName) { + c.q.cursors = append(c.q.cursors, cursorNode{ + name: name, + area: len(c.q.areas) - 1, + }) +} + +func (c *pointerCollector) sourceOp(op transfer.SourceOp, events *handlerEvents) { + h := c.newHandler(op.Tag, events) + h.sourceMimes = append(h.sourceMimes, op.Type) +} + +func (c *pointerCollector) targetOp(op transfer.TargetOp, events *handlerEvents) { + h := c.newHandler(op.Tag, events) + h.targetMimes = append(h.targetMimes, op.Type) +} + +func (c *pointerCollector) offerOp(op transfer.OfferOp, events *handlerEvents) { + h := c.newHandler(op.Tag, events) + h.offeredMime = op.Type + h.data = op.Data +} + +func (c *pointerCollector) reset() { + c.q.reset() + c.resetState() + c.nodeStack = c.nodeStack[:0] + c.ensureRoot() +} + +// Ensure implicit root area for semantic descriptions to hang onto. +func (c *pointerCollector) ensureRoot() { + if len(c.q.areas) > 0 { + return + } + c.pushArea(areaRect, f32.Rect(-1e6, -1e6, 1e6, 1e6)) + // Make it semantic to ensure a single semantic root. + c.q.areas[0].semantic.valid = true +} + +func (q *pointerQueue) assignSemIDs() { + if q.semantic.idsAssigned { + return + } + q.semantic.idsAssigned = true + for i, a := range q.areas { + if a.semantic.valid { + q.areas[i].semantic.id = q.semanticIDFor(a.semantic.content) } } } -func (q *pointerQueue) opHit(handlers *[]event.Tag, pos f32.Point) { +func (q *pointerQueue) AppendSemantics(nodes []SemanticNode) []SemanticNode { + q.assignSemIDs() + nodes = q.appendSemanticChildren(nodes, 0) + nodes = q.appendSemanticArea(nodes, 0, 0) + return nodes +} + +func (q *pointerQueue) appendSemanticArea(nodes []SemanticNode, parentID SemanticID, nodeIdx int) []SemanticNode { + areaIdx := nodes[nodeIdx].areaIdx + a := q.areas[areaIdx] + childStart := len(nodes) + nodes = q.appendSemanticChildren(nodes, a.firstChild) + childEnd := len(nodes) + for i := childStart; i < childEnd; i++ { + nodes = q.appendSemanticArea(nodes, a.semantic.id, i) + } + n := &nodes[nodeIdx] + n.ParentID = parentID + n.Children = nodes[childStart:childEnd] + return nodes +} + +func (q *pointerQueue) appendSemanticChildren(nodes []SemanticNode, areaIdx int) []SemanticNode { + if areaIdx == -1 { + return nodes + } + a := q.areas[areaIdx] + if semID := a.semantic.id; semID != 0 { + cnt := a.semantic.content + nodes = append(nodes, SemanticNode{ + ID: semID, + Desc: SemanticDesc{ + Bounds: f32.Rectangle{ + Min: a.trans.Transform(a.area.rect.Min), + Max: a.trans.Transform(a.area.rect.Max), + }, + Label: cnt.label, + Description: cnt.desc, + Class: cnt.class, + Gestures: cnt.gestures, + Selected: cnt.selected, + Disabled: cnt.disabled, + }, + areaIdx: areaIdx, + }) + } else { + nodes = q.appendSemanticChildren(nodes, a.firstChild) + } + return q.appendSemanticChildren(nodes, a.sibling) +} + +func (q *pointerQueue) semanticIDFor(content semanticContent) SemanticID { + ids := q.semantic.contentIDs[content] + for i, id := range ids { + if !id.used { + ids[i].used = true + return id.id + } + } + // No prior assigned ID; allocate a new one. + q.semantic.lastID++ + id := semanticID{id: q.semantic.lastID, used: true} + if q.semantic.contentIDs == nil { + q.semantic.contentIDs = make(map[semanticContent][]semanticID) + } + q.semantic.contentIDs[content] = append(q.semantic.contentIDs[content], id) + return id.id +} + +func (q *pointerQueue) SemanticAt(pos f32.Point) (SemanticID, bool) { + q.assignSemIDs() + for i := len(q.hitTree) - 1; i >= 0; i-- { + n := &q.hitTree[i] + hit := q.hit(n.area, pos) + if !hit { + continue + } + area := q.areas[n.area] + if area.semantic.id != 0 { + return area.semantic.id, true + } + } + return 0, false +} + +func (q *pointerQueue) opHit(pos f32.Point) []event.Tag { // Track whether we're passing through hits. pass := true + hits := q.scratch[:0] idx := len(q.hitTree) - 1 for idx >= 0 { n := &q.hitTree[idx] - if !q.hit(n.area, pos) { + hit := q.hit(n.area, pos) + if !hit { idx-- continue } @@ -133,10 +455,12 @@ func (q *pointerQueue) opHit(handlers *[]event.Tag, pos f32.Point) { } if n.tag != nil { if _, exists := q.handlers[n.tag]; exists { - *handlers = append(*handlers, n.tag) + hits = addHandler(hits, n.tag) } } } + q.scratch = hits[:0] + return hits } func (q *pointerQueue) invTransform(areaIdx int, p f32.Point) f32.Point { @@ -153,32 +477,53 @@ func (q *pointerQueue) hit(areaIdx int, p f32.Point) bool { if !a.area.Hit(p) { return false } - areaIdx = a.next + areaIdx = a.parent } return true } -func (q *pointerQueue) init() { +func (q *pointerQueue) reset() { if q.handlers == nil { q.handlers = make(map[event.Tag]*pointerHandler) } -} - -func (q *pointerQueue) Frame(root *op.Ops, events *handlerEvents) { - q.init() for _, h := range q.handlers { // Reset handler. h.active = false h.wantsGrab = false h.types = 0 + h.sourceMimes = h.sourceMimes[:0] + h.targetMimes = h.targetMimes[:0] } q.hitTree = q.hitTree[:0] q.areas = q.areas[:0] - q.reader.Reset(root) - q.collectHandlers(&q.reader, events, f32.Affine2D{}, -1, -1, false) + q.cursors = q.cursors[:0] + q.semantic.idsAssigned = false + for k, ids := range q.semantic.contentIDs { + for i := len(ids) - 1; i >= 0; i-- { + if !ids[i].used { + ids = append(ids[:i], ids[i+1:]...) + } else { + ids[i].used = false + } + } + if len(ids) > 0 { + q.semantic.contentIDs[k] = ids + } else { + delete(q.semantic.contentIDs, k) + } + } + for _, rc := range q.transfers { + if rc != nil { + rc.Close() + } + } + q.transfers = nil +} + +func (q *pointerQueue) Frame(events *handlerEvents) { for k, h := range q.handlers { if !h.active { - q.dropHandlers(events, k) + q.dropHandler(nil, k) delete(q.handlers, k) } if h.wantsGrab { @@ -189,86 +534,93 @@ func (q *pointerQueue) Frame(root *op.Ops, events *handlerEvents) { for i, k2 := range p.handlers { if k2 == k { // Drop other handlers that lost their grab. - q.dropHandlers(events, p.handlers[i+1:]...) - q.dropHandlers(events, p.handlers[:i]...) + dropped := q.scratch[:0] + dropped = append(dropped, p.handlers[:i]...) + dropped = append(dropped, p.handlers[i+1:]...) + for _, tag := range dropped { + q.dropHandler(events, tag) + } break } } } } } + for i := range q.pointers { + p := &q.pointers[i] + q.deliverEnterLeaveEvents(p, events, p.last) + q.deliverTransferDataEvent(p, events) + } } -func (q *pointerQueue) dropHandlers(events *handlerEvents, tags ...event.Tag) { - for _, k := range tags { - events.Add(k, pointer.Event{Type: pointer.Cancel}) - for i := range q.pointers { - p := &q.pointers[i] - for i := len(p.handlers) - 1; i >= 0; i-- { - if p.handlers[i] == k { - p.handlers = append(p.handlers[:i], p.handlers[i+1:]...) - } +func (q *pointerQueue) dropHandler(events *handlerEvents, tag event.Tag) { + if events != nil { + events.Add(tag, pointer.Event{Type: pointer.Cancel}) + } + for i := range q.pointers { + p := &q.pointers[i] + for i := len(p.handlers) - 1; i >= 0; i-- { + if p.handlers[i] == tag { + p.handlers = append(p.handlers[:i], p.handlers[i+1:]...) } - for i := len(p.entered) - 1; i >= 0; i-- { - if p.entered[i] == k { - p.entered = append(p.entered[:i], p.entered[i+1:]...) - } + } + for i := len(p.entered) - 1; i >= 0; i-- { + if p.entered[i] == tag { + p.entered = append(p.entered[:i], p.entered[i+1:]...) } } } } +// pointerOf returns the pointerInfo index corresponding to the pointer in e. +func (q *pointerQueue) pointerOf(e pointer.Event) int { + for i, p := range q.pointers { + if p.id == e.PointerID { + return i + } + } + q.pointers = append(q.pointers, pointerInfo{id: e.PointerID}) + return len(q.pointers) - 1 +} + func (q *pointerQueue) Push(e pointer.Event, events *handlerEvents) { - q.init() if e.Type == pointer.Cancel { q.pointers = q.pointers[:0] for k := range q.handlers { - q.dropHandlers(events, k) + q.dropHandler(events, k) } return } - pidx := -1 - for i, p := range q.pointers { - if p.id == e.PointerID { - pidx = i - break - } - } - if pidx == -1 { - q.pointers = append(q.pointers, pointerInfo{id: e.PointerID}) - pidx = len(q.pointers) - 1 - } + pidx := q.pointerOf(e) p := &q.pointers[pidx] + p.last = e - if e.Type == pointer.Move && p.pressed { - e.Type = pointer.Drag - } - - if e.Type == pointer.Release { + switch e.Type { + case pointer.Press: + q.deliverEnterLeaveEvents(p, events, e) + p.pressed = true q.deliverEvent(p, events, e) - p.pressed = false - } - q.scratch = q.scratch[:0] - q.opHit(&q.scratch, e.Position) - if p.pressed { - // Filter out non-participating handlers. - for i := len(q.scratch) - 1; i >= 0; i-- { - if _, found := searchTag(p.handlers, q.scratch[i]); !found { - q.scratch = append(q.scratch[:i], q.scratch[i+1:]...) - } + case pointer.Move: + if p.pressed { + e.Type = pointer.Drag } - } - q.deliverEnterLeaveEvents(p, q.scratch, events, e) - - if !p.pressed { - p.handlers = append(p.handlers[:0], q.scratch...) - } - if e.Type == pointer.Press { - p.pressed = true - } - if e.Type != pointer.Release { + q.deliverEnterLeaveEvents(p, events, e) q.deliverEvent(p, events, e) + if p.pressed { + q.deliverDragEvent(p, events) + } + case pointer.Release: + q.deliverEvent(p, events, e) + p.pressed = false + q.deliverEnterLeaveEvents(p, events, e) + q.deliverDropEvent(p, events) + case pointer.Scroll: + q.deliverEnterLeaveEvents(p, events, e) + q.deliverScrollEvent(p, events, e) + default: + panic("unsupported pointer event type") } + if !p.pressed && len(p.entered) == 0 { // No longer need to track pointer. q.pointers = append(q.pointers[:pidx], q.pointers[pidx+1:]...) @@ -277,28 +629,77 @@ func (q *pointerQueue) Push(e pointer.Event, events *handlerEvents) { func (q *pointerQueue) deliverEvent(p *pointerInfo, events *handlerEvents, e pointer.Event) { foremost := true + if p.pressed && len(p.handlers) == 1 { + e.Priority = pointer.Grabbed + foremost = false + } for _, k := range p.handlers { h := q.handlers[k] + if e.Type&h.types == 0 { + continue + } e := e - if p.pressed && len(p.handlers) == 1 { - e.Priority = pointer.Grabbed - } else if foremost { + if foremost { + foremost = false e.Priority = pointer.Foremost } - e.Position = q.invTransform(h.area, e.Position) + events.Add(k, e) + } +} - if e.Type&h.types == e.Type { +func (q *pointerQueue) deliverScrollEvent(p *pointerInfo, events *handlerEvents, e pointer.Event) { + foremost := true + if p.pressed && len(p.handlers) == 1 { + e.Priority = pointer.Grabbed + foremost = false + } + var sx, sy = e.Scroll.X, e.Scroll.Y + for _, k := range p.handlers { + if sx == 0 && sy == 0 { + return + } + h := q.handlers[k] + // Distribute the scroll to the handler based on its ScrollRange. + sx, e.Scroll.X = setScrollEvent(sx, h.scrollRange.Min.X, h.scrollRange.Max.X) + sy, e.Scroll.Y = setScrollEvent(sy, h.scrollRange.Min.Y, h.scrollRange.Max.Y) + e := e + if foremost { foremost = false - events.Add(k, e) + e.Priority = pointer.Foremost } + e.Position = q.invTransform(h.area, e.Position) + events.Add(k, e) } } -func (q *pointerQueue) deliverEnterLeaveEvents(p *pointerInfo, hits []event.Tag, events *handlerEvents, e pointer.Event) { +func (q *pointerQueue) deliverEnterLeaveEvents(p *pointerInfo, events *handlerEvents, e pointer.Event) { + var hits []event.Tag if e.Source != pointer.Mouse && !p.pressed && e.Type != pointer.Press { // Consider non-mouse pointers leaving when they're released. - hits = nil + } else { + hits = q.opHit(e.Position) + if p.pressed { + // Filter out non-participating handlers, + // except potential transfer targets when a transfer has been initiated. + var hitsHaveTarget bool + if p.dataSource != nil { + transferSource := q.handlers[p.dataSource] + for _, hit := range hits { + if _, ok := firstMimeMatch(transferSource, q.handlers[hit]); ok { + hitsHaveTarget = true + break + } + } + } + for i := len(hits) - 1; i >= 0; i-- { + if _, found := searchTag(p.handlers, hits[i]); !found && !hitsHaveTarget { + hits = append(hits[:i], hits[i+1:]...) + } + } + } else { + p.handlers = append(p.handlers[:0], hits...) + } } // Deliver Leave events. for _, k := range p.entered { @@ -307,28 +708,116 @@ func (q *pointerQueue) deliverEnterLeaveEvents(p *pointerInfo, hits []event.Tag, } h := q.handlers[k] e.Type = pointer.Leave - e.Position = q.invTransform(h.area, e.Position) - if e.Type&h.types == e.Type { + if e.Type&h.types != 0 { + e.Position = q.invTransform(h.area, e.Position) events.Add(k, e) } } - // Deliver Enter events. + // Deliver Enter events and update cursor. + q.cursor = pointer.CursorDefault for _, k := range hits { + h := q.handlers[k] + for i := len(q.cursors) - 1; i >= 0; i-- { + if c := q.cursors[i]; c.area == h.area { + q.cursor = c.name + break + } + } if _, found := searchTag(p.entered, k); found { continue } - h := q.handlers[k] e.Type = pointer.Enter - e.Position = q.invTransform(h.area, e.Position) - if e.Type&h.types == e.Type { + if e.Type&h.types != 0 { + e.Position = q.invTransform(h.area, e.Position) events.Add(k, e) } } p.entered = append(p.entered[:0], hits...) } +func (q *pointerQueue) deliverDragEvent(p *pointerInfo, events *handlerEvents) { + if p.dataSource != nil { + return + } + // Identify the data source. + for _, k := range p.entered { + src := q.handlers[k] + if len(src.sourceMimes) == 0 { + continue + } + // One data source handler per pointer. + p.dataSource = k + // Notify all potential targets. + for k, tgt := range q.handlers { + if _, ok := firstMimeMatch(src, tgt); ok { + events.Add(k, transfer.InitiateEvent{}) + } + } + break + } +} + +func (q *pointerQueue) deliverDropEvent(p *pointerInfo, events *handlerEvents) { + if p.dataSource == nil { + return + } + // Request data from the source. + src := q.handlers[p.dataSource] + for _, k := range p.entered { + h := q.handlers[k] + if m, ok := firstMimeMatch(src, h); ok { + p.dataTarget = k + events.Add(p.dataSource, transfer.RequestEvent{Type: m}) + return + } + } + // No valid target found, abort. + q.deliverTransferCancelEvent(p, events) +} + +func (q *pointerQueue) deliverTransferDataEvent(p *pointerInfo, events *handlerEvents) { + if p.dataSource == nil { + return + } + src := q.handlers[p.dataSource] + if src.data == nil { + // Data not received yet. + return + } + if p.dataTarget == nil { + q.deliverTransferCancelEvent(p, events) + return + } + // Send the offered data to the target. + transferIdx := len(q.transfers) + events.Add(p.dataTarget, transfer.DataEvent{ + Type: src.offeredMime, + Open: func() io.ReadCloser { + q.transfers[transferIdx] = nil + return src.data + }, + }) + q.transfers = append(q.transfers, src.data) + p.dataTarget = nil +} + +func (q *pointerQueue) deliverTransferCancelEvent(p *pointerInfo, events *handlerEvents) { + events.Add(p.dataSource, transfer.CancelEvent{}) + // Cancel all potential targets. + src := q.handlers[p.dataSource] + for k, h := range q.handlers { + if _, ok := firstMimeMatch(src, h); ok { + events.Add(k, transfer.CancelEvent{}) + } + } + src.offeredMime = "" + src.data = nil + p.dataSource = nil + p.dataTarget = nil +} + func searchTag(tags []event.Tag, tag event.Tag) (int, bool) { for i, t := range tags { if t == tag { @@ -338,41 +827,38 @@ func searchTag(tags []event.Tag, tag event.Tag) (int, bool) { return 0, false } -func (op *areaOp) Decode(d []byte) { - if opconst.OpType(d[0]) != opconst.TypeArea { - panic("invalid op") - } - bo := binary.LittleEndian - rect := image.Rectangle{ - Min: image.Point{ - X: int(int32(bo.Uint32(d[2:]))), - Y: int(int32(bo.Uint32(d[6:]))), - }, - Max: image.Point{ - X: int(int32(bo.Uint32(d[10:]))), - Y: int(int32(bo.Uint32(d[14:]))), - }, +// addHandler adds tag to the slice if not present. +func addHandler(tags []event.Tag, tag event.Tag) []event.Tag { + for _, t := range tags { + if t == tag { + return tags + } } - *op = areaOp{ - kind: areaKind(d[1]), - rect: rect, + return append(tags, tag) +} + +// firstMimeMatch returns the first type match between src and tgt. +func firstMimeMatch(src, tgt *pointerHandler) (first string, matched bool) { + for _, m1 := range tgt.targetMimes { + for _, m2 := range src.sourceMimes { + if m1 == m2 { + return m1, true + } + } } + return "", false } func (op *areaOp) Hit(pos f32.Point) bool { - min := f32.Point{ - X: float32(op.rect.Min.X), - Y: float32(op.rect.Min.Y), - } - pos = pos.Sub(min) + pos = pos.Sub(op.rect.Min) size := op.rect.Size() switch op.kind { case areaRect: - return 0 <= pos.X && pos.X < float32(size.X) && - 0 <= pos.Y && pos.Y < float32(size.Y) + return 0 <= pos.X && pos.X < size.X && + 0 <= pos.Y && pos.Y < size.Y case areaEllipse: - rx := float32(size.X) / 2 - ry := float32(size.Y) / 2 + rx := size.X / 2 + ry := size.Y / 2 xh := pos.X - rx yk := pos.Y - ry // The ellipse function works in all cases because @@ -383,22 +869,12 @@ func (op *areaOp) Hit(pos f32.Point) bool { } } -func decodePointerInputOp(d []byte, refs []interface{}) pointer.InputOp { - if opconst.OpType(d[0]) != opconst.TypePointerInput { - panic("invalid op") - } - return pointer.InputOp{ - Tag: refs[0].(event.Tag), - Grab: d[1] != 0, - Types: pointer.Type(d[2]), - } -} - -func decodePassOp(d []byte) pointer.PassOp { - if opconst.OpType(d[0]) != opconst.TypePass { - panic("invalid op") +func setScrollEvent(scroll float32, min, max int) (left, scrolled float32) { + if v := float32(max); scroll > v { + return scroll - v, v } - return pointer.PassOp{ - Pass: d[1] != 0, + if v := float32(min); scroll < v { + return scroll - v, v } + return 0, scroll } diff --git a/vendor/gioui.org/io/router/router.go b/vendor/gioui.org/io/router/router.go index d24d214..a02f0f6 100644 --- a/vendor/gioui.org/io/router/router.go +++ b/vendor/gioui.org/io/router/router.go @@ -12,22 +12,37 @@ package router import ( "encoding/binary" + "image" + "io" + "strings" "time" - "gioui.org/internal/opconst" + "gioui.org/f32" "gioui.org/internal/ops" + "gioui.org/io/clipboard" "gioui.org/io/event" "gioui.org/io/key" "gioui.org/io/pointer" "gioui.org/io/profile" + "gioui.org/io/semantic" + "gioui.org/io/transfer" "gioui.org/op" ) // Router is a Queue implementation that routes events // to handlers declared in operation lists. type Router struct { - pqueue pointerQueue - kqueue keyQueue + savedTrans []f32.Affine2D + transStack []f32.Affine2D + pointer struct { + queue pointerQueue + collector pointerCollector + } + key struct { + queue keyQueue + collector keyCollector + } + cqueue clipboardQueue handlers handlerEvents @@ -38,11 +53,44 @@ type Router struct { wakeupTime time.Time // ProfileOp summary. - profiling bool profHandlers map[event.Tag]struct{} profile profile.Event } +// SemanticNode represents a node in the tree describing the components +// contained in a frame. +type SemanticNode struct { + ID SemanticID + ParentID SemanticID + Children []SemanticNode + Desc SemanticDesc + + areaIdx int +} + +// SemanticDesc provides a semantic description of a UI component. +type SemanticDesc struct { + Class semantic.ClassOp + Description string + Label string + Selected bool + Disabled bool + Gestures SemanticGestures + Bounds f32.Rectangle +} + +// SemanticGestures is a bit-set of supported gestures. +type SemanticGestures int + +const ( + ClickGesture SemanticGestures = 1 << iota +) + +// SemanticID uniquely identifies a SemanticDescription. +// +// By convention, the zero value denotes the non-existent ID. +type SemanticID uint64 + type handlerEvents struct { handlers map[event.Tag][]event.Event hadEvents bool @@ -61,33 +109,39 @@ func (q *Router) Events(k event.Tag) []event.Event { // Frame replaces the declared handlers from the supplied // operation list. The text input state, wakeup time and whether // there are active profile handlers is also saved. -func (q *Router) Frame(ops *op.Ops) { +func (q *Router) Frame(frame *op.Ops) { q.handlers.Clear() q.wakeup = false - q.profiling = false for k := range q.profHandlers { delete(q.profHandlers, k) } + var ops *ops.Ops + if frame != nil { + ops = &frame.Internal + } q.reader.Reset(ops) q.collect() - q.pqueue.Frame(ops, &q.handlers) - q.kqueue.Frame(ops, &q.handlers) + q.pointer.queue.Frame(&q.handlers) + q.key.queue.Frame(&q.handlers, q.key.collector) if q.handlers.HadEvents() { q.wakeup = true q.wakeupTime = time.Time{} } } -func (q *Router) Add(events ...event.Event) bool { +// Queue an event and report whether at least one handler had an event queued. +func (q *Router) Queue(events ...event.Event) bool { for _, e := range events { switch e := e.(type) { case profile.Event: q.profile = e case pointer.Event: - q.pqueue.Push(e, &q.handlers) + q.pointer.queue.Push(e, &q.handlers) case key.EditEvent, key.Event, key.FocusEvent: - q.kqueue.Push(e, &q.handlers) + q.key.queue.Push(e, &q.handlers) + case clipboard.Event: + q.cqueue.Push(e, &q.handlers) } } return q.handlers.HadEvents() @@ -96,25 +150,189 @@ func (q *Router) Add(events ...event.Event) bool { // TextInputState returns the input state from the most recent // call to Frame. func (q *Router) TextInputState() TextInputState { - return q.kqueue.InputState() + return q.key.queue.InputState() +} + +// TextInputHint returns the input mode from the most recent key.InputOp. +func (q *Router) TextInputHint() (key.InputHint, bool) { + return q.key.queue.InputHint() +} + +// WriteClipboard returns the most recent text to be copied +// to the clipboard, if any. +func (q *Router) WriteClipboard() (string, bool) { + return q.cqueue.WriteClipboard() +} + +// ReadClipboard reports if any new handler is waiting +// to read the clipboard. +func (q *Router) ReadClipboard() bool { + return q.cqueue.ReadClipboard() +} + +// Cursor returns the last cursor set. +func (q *Router) Cursor() pointer.CursorName { + return q.pointer.queue.cursor +} + +// SemanticAt returns the first semantic description under pos, if any. +func (q *Router) SemanticAt(pos f32.Point) (SemanticID, bool) { + return q.pointer.queue.SemanticAt(pos) +} + +// AppendSemantics appends the semantic tree to nodes, and returns the result. +// The root node is the first added. +func (q *Router) AppendSemantics(nodes []SemanticNode) []SemanticNode { + q.pointer.collector.q = &q.pointer.queue + q.pointer.collector.ensureRoot() + return q.pointer.queue.AppendSemantics(nodes) } func (q *Router) collect() { + q.transStack = q.transStack[:0] + pc := &q.pointer.collector + pc.q = &q.pointer.queue + pc.reset() + kc := &q.key.collector + *kc = keyCollector{q: &q.key.queue} + q.key.queue.Reset() + var t f32.Affine2D for encOp, ok := q.reader.Decode(); ok; encOp, ok = q.reader.Decode() { - switch opconst.OpType(encOp.Data[0]) { - case opconst.TypeInvalidate: + switch ops.OpType(encOp.Data[0]) { + case ops.TypeInvalidate: op := decodeInvalidateOp(encOp.Data) if !q.wakeup || op.At.Before(q.wakeupTime) { q.wakeup = true q.wakeupTime = op.At } - case opconst.TypeProfile: + case ops.TypeProfile: op := decodeProfileOp(encOp.Data, encOp.Refs) if q.profHandlers == nil { q.profHandlers = make(map[event.Tag]struct{}) } - q.profiling = true q.profHandlers[op.Tag] = struct{}{} + case ops.TypeClipboardRead: + q.cqueue.ProcessReadClipboard(encOp.Refs) + case ops.TypeClipboardWrite: + q.cqueue.ProcessWriteClipboard(encOp.Refs) + case ops.TypeSave: + id := ops.DecodeSave(encOp.Data) + if extra := id - len(q.savedTrans) + 1; extra > 0 { + q.savedTrans = append(q.savedTrans, make([]f32.Affine2D, extra)...) + } + q.savedTrans[id] = t + case ops.TypeLoad: + id := ops.DecodeLoad(encOp.Data) + t = q.savedTrans[id] + pc.resetState() + pc.setTrans(t) + + case ops.TypeClip: + var op ops.ClipOp + op.Decode(encOp.Data) + pc.clip(op) + case ops.TypePopClip: + pc.popArea() + case ops.TypeTransform: + t2, push := ops.DecodeTransform(encOp.Data) + if push { + q.transStack = append(q.transStack, t) + } + t = t.Mul(t2) + pc.setTrans(t) + case ops.TypePopTransform: + n := len(q.transStack) + t = q.transStack[n-1] + q.transStack = q.transStack[:n-1] + pc.setTrans(t) + + // Pointer ops. + case ops.TypePass: + pc.pass() + case ops.TypePopPass: + pc.popPass() + case ops.TypePointerInput: + bo := binary.LittleEndian + op := pointer.InputOp{ + Tag: encOp.Refs[0].(event.Tag), + Grab: encOp.Data[1] != 0, + Types: pointer.Type(bo.Uint16(encOp.Data[2:])), + ScrollBounds: image.Rectangle{ + Min: image.Point{ + X: int(int32(bo.Uint32(encOp.Data[4:]))), + Y: int(int32(bo.Uint32(encOp.Data[8:]))), + }, + Max: image.Point{ + X: int(int32(bo.Uint32(encOp.Data[12:]))), + Y: int(int32(bo.Uint32(encOp.Data[16:]))), + }, + }, + } + pc.inputOp(op, &q.handlers) + case ops.TypeCursor: + name := encOp.Refs[0].(pointer.CursorName) + pc.cursor(name) + case ops.TypeSource: + op := transfer.SourceOp{ + Tag: encOp.Refs[0].(event.Tag), + Type: encOp.Refs[1].(string), + } + pc.sourceOp(op, &q.handlers) + case ops.TypeTarget: + op := transfer.TargetOp{ + Tag: encOp.Refs[0].(event.Tag), + Type: encOp.Refs[1].(string), + } + pc.targetOp(op, &q.handlers) + case ops.TypeOffer: + op := transfer.OfferOp{ + Tag: encOp.Refs[0].(event.Tag), + Type: encOp.Refs[1].(string), + Data: encOp.Refs[2].(io.ReadCloser), + } + pc.offerOp(op, &q.handlers) + + // Key ops. + case ops.TypeKeyFocus: + tag, _ := encOp.Refs[0].(event.Tag) + op := key.FocusOp{ + Tag: tag, + } + kc.focusOp(op.Tag) + case ops.TypeKeySoftKeyboard: + op := key.SoftKeyboardOp{ + Show: encOp.Data[1] != 0, + } + kc.softKeyboard(op.Show) + case ops.TypeKeyInput: + op := key.InputOp{ + Tag: encOp.Refs[0].(event.Tag), + Hint: key.InputHint(encOp.Data[1]), + } + kc.inputOp(op) + + // Semantic ops. + case ops.TypeSemanticLabel: + lbl := encOp.Refs[0].(*string) + pc.semanticLabel(*lbl) + case ops.TypeSemanticDesc: + desc := encOp.Refs[0].(*string) + pc.semanticDesc(*desc) + case ops.TypeSemanticClass: + class := semantic.ClassOp(encOp.Data[1]) + pc.semanticClass(class) + case ops.TypeSemanticSelected: + if encOp.Data[1] != 0 { + pc.semanticSelected(true) + } else { + pc.semanticSelected(false) + } + case ops.TypeSemanticDisabled: + if encOp.Data[1] != 0 { + pc.semanticDisabled(true) + } else { + pc.semanticDisabled(false) + } } } } @@ -122,7 +340,7 @@ func (q *Router) collect() { // Profiling reports whether there was profile handlers in the // most recent Frame call. func (q *Router) Profiling() bool { - return q.profiling + return len(q.profHandlers) > 0 } // WakeupTime returns the most recent time for doing another frame, @@ -137,9 +355,13 @@ func (h *handlerEvents) init() { } } -func (h *handlerEvents) Add(k event.Tag, e event.Event) { +func (h *handlerEvents) AddNoRedraw(k event.Tag, e event.Event) { h.init() h.handlers[k] = append(h.handlers[k], e) +} + +func (h *handlerEvents) Add(k event.Tag, e event.Event) { + h.AddNoRedraw(k, e) h.hadEvents = true } @@ -174,7 +396,7 @@ func (h *handlerEvents) Clear() { } func decodeProfileOp(d []byte, refs []interface{}) profile.Op { - if opconst.OpType(d[0]) != opconst.TypeProfile { + if ops.OpType(d[0]) != ops.TypeProfile { panic("invalid op") } return profile.Op{ @@ -184,7 +406,7 @@ func decodeProfileOp(d []byte, refs []interface{}) profile.Op { func decodeInvalidateOp(d []byte) op.InvalidateOp { bo := binary.LittleEndian - if opconst.OpType(d[0]) != opconst.TypeInvalidate { + if ops.OpType(d[0]) != ops.TypeInvalidate { panic("invalid op") } var o op.InvalidateOp @@ -193,3 +415,11 @@ func decodeInvalidateOp(d []byte) op.InvalidateOp { } return o } + +func (s SemanticGestures) String() string { + var gestures []string + if s&ClickGesture != 0 { + gestures = append(gestures, "Click") + } + return strings.Join(gestures, ",") +} diff --git a/vendor/gioui.org/io/semantic/semantic.go b/vendor/gioui.org/io/semantic/semantic.go new file mode 100644 index 0000000..7499fc8 --- /dev/null +++ b/vendor/gioui.org/io/semantic/semantic.go @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +// Package semantic provides operations for semantic descriptions of a user +// interface, to facilitate presentation and interaction in external software +// such as screen readers. +// +// Semantic descriptions are organized in a tree, with clip operations as +// nodes. Operations in this package are associated with the current semantic +// node, that is the most recent pushed clip operation. +package semantic + +import ( + "gioui.org/internal/ops" + "gioui.org/op" +) + +// LabelOp provides the content of a textual component. +type LabelOp string + +// DescriptionOp describes a component. +type DescriptionOp string + +// ClassOp provides the component class. +type ClassOp int + +const ( + Unknown ClassOp = iota + Button + CheckBox + Editor + RadioButton + Switch +) + +// SelectedOp describes the selected state for components that have +// boolean state. +type SelectedOp bool + +// DisabledOp describes the disabled state. +type DisabledOp bool + +func (l LabelOp) Add(o *op.Ops) { + s := string(l) + data := ops.Write1(&o.Internal, ops.TypeSemanticLabelLen, &s) + data[0] = byte(ops.TypeSemanticLabel) +} + +func (d DescriptionOp) Add(o *op.Ops) { + s := string(d) + data := ops.Write1(&o.Internal, ops.TypeSemanticDescLen, &s) + data[0] = byte(ops.TypeSemanticDesc) +} + +func (c ClassOp) Add(o *op.Ops) { + data := ops.Write(&o.Internal, ops.TypeSemanticClassLen) + data[0] = byte(ops.TypeSemanticClass) + data[1] = byte(c) +} + +func (s SelectedOp) Add(o *op.Ops) { + data := ops.Write(&o.Internal, ops.TypeSemanticSelectedLen) + data[0] = byte(ops.TypeSemanticSelected) + if s { + data[1] = 1 + } +} + +func (d DisabledOp) Add(o *op.Ops) { + data := ops.Write(&o.Internal, ops.TypeSemanticDisabledLen) + data[0] = byte(ops.TypeSemanticDisabled) + if d { + data[1] = 1 + } +} + +func (c ClassOp) String() string { + switch c { + case Unknown: + return "Unknown" + case Button: + return "Button" + case CheckBox: + return "CheckBox" + case Editor: + return "Editor" + case RadioButton: + return "RadioButton" + case Switch: + return "Switch" + default: + panic("invalid ClassOp") + } +} diff --git a/vendor/gioui.org/io/system/system.go b/vendor/gioui.org/io/system/system.go index 8abac6d..e145825 100644 --- a/vendor/gioui.org/io/system/system.go +++ b/vendor/gioui.org/io/system/system.go @@ -26,27 +26,8 @@ type FrameEvent struct { Size image.Point // Insets is the insets to apply. Insets Insets - // Frame is the callback to supply the list of - // operations to complete the FrameEvent. - // - // Note that the operation list and the operations themselves - // may not be mutated until another FrameEvent is received from - // the same event source. - // That means that calls to frame.Reset and changes to referenced - // data such as ImageOp backing images should happen between - // receiving a FrameEvent and calling Frame. - // - // Example: - // - // var w *app.Window - // var frame *op.Ops - // for e := range w.Events() { - // if e, ok := e.(system.FrameEvent); ok { - // // Call frame.Reset and manipulate images for ImageOps - // // here. - // e.Frame(frame) - // } - // } + // Frame completes the FrameEvent by drawing the graphical operations + // from ops into the window. Frame func(frame *op.Ops) // Queue supplies the events for event handlers. Queue event.Queue @@ -60,12 +41,6 @@ type DestroyEvent struct { Err error } -// ClipboardEvent is sent once for each request for the -// clipboard content. -type ClipboardEvent struct { - Text string -} - // Insets is the space taken up by // system decoration such as translucent // system bars and software keyboards. @@ -79,10 +54,11 @@ type StageEvent struct { Stage Stage } -// CommandEvent is a system event. +// CommandEvent is a system event. Unlike most other events, CommandEvent is +// delivered as a pointer to allow Cancel to suppress it. type CommandEvent struct { Type CommandType - // Suppress the default action of the command. + // Cancel suppress the default action of the command. Cancel bool } @@ -117,8 +93,7 @@ func (l Stage) String() string { } } -func (FrameEvent) ImplementsEvent() {} -func (StageEvent) ImplementsEvent() {} -func (*CommandEvent) ImplementsEvent() {} -func (DestroyEvent) ImplementsEvent() {} -func (ClipboardEvent) ImplementsEvent() {} +func (FrameEvent) ImplementsEvent() {} +func (StageEvent) ImplementsEvent() {} +func (*CommandEvent) ImplementsEvent() {} +func (DestroyEvent) ImplementsEvent() {} diff --git a/vendor/gioui.org/io/transfer/transfer.go b/vendor/gioui.org/io/transfer/transfer.go new file mode 100644 index 0000000..85ab68c --- /dev/null +++ b/vendor/gioui.org/io/transfer/transfer.go @@ -0,0 +1,109 @@ +// Package transfer contains operations and events for brokering data transfers. +// +// The transfer protocol is as follows: +// +// - Data sources are registered with SourceOps, data targets with TargetOps. +// - A data source receives a RequestEvent when a transfer is initiated. +// It must respond with an OfferOp. +// - The target receives a DataEvent when transferring to it. It must close +// the event data after use. +// +// When a user initiates a pointer-guided drag and drop transfer, the +// source as well as all potential targets receive an InitiateEvent. +// Potential targets are targets with at least one MIME type in common +// with the source. When a drag gesture completes, a CancelEvent is sent +// to the source and all potential targets. +// +// Note that the RequestEvent is sent to the source upon drop. +package transfer + +import ( + "io" + + "gioui.org/internal/ops" + "gioui.org/io/event" + "gioui.org/op" +) + +// SourceOp registers a tag as a data source for a MIME type. +// Use multiple SourceOps if a tag supports multiple types. +type SourceOp struct { + Tag event.Tag + // Type is the MIME type supported by this source. + Type string +} + +// TargetOp registers a tag as a data target. +// Use multiple TargetOps if a tag supports multiple types. +type TargetOp struct { + Tag event.Tag + // Type is the MIME type accepted by this target. + Type string +} + +// OfferOp is used by data sources as a response to a RequestEvent. +type OfferOp struct { + Tag event.Tag + // Type is the MIME type of Data. + // It must be the Type from the corresponding RequestEvent. + Type string + // Data contains the offered data. It is closed when the + // transfer is complete or cancelled. + // Data must be kept valid until closed, and it may be used from + // a goroutine separate from the one processing the frame.. + Data io.ReadCloser +} + +func (op SourceOp) Add(o *op.Ops) { + data := ops.Write2(&o.Internal, ops.TypeSourceLen, op.Tag, op.Type) + data[0] = byte(ops.TypeSource) +} + +func (op TargetOp) Add(o *op.Ops) { + data := ops.Write2(&o.Internal, ops.TypeTargetLen, op.Tag, op.Type) + data[0] = byte(ops.TypeTarget) +} + +// Add the offer to the list of operations. +// It panics if the Data field is not set. +func (op OfferOp) Add(o *op.Ops) { + if op.Data == nil { + panic("invalid nil data in OfferOp") + } + data := ops.Write3(&o.Internal, ops.TypeOfferLen, op.Tag, op.Type, op.Data) + data[0] = byte(ops.TypeOffer) +} + +// RequestEvent requests data from a data source. The source must +// respond with an OfferOp. +type RequestEvent struct { + // Type is the first matched type between the source and the target. + Type string +} + +func (RequestEvent) ImplementsEvent() {} + +// InitiateEvent is sent to a data source when a drag-and-drop +// transfer gesture is initiated. +// +// Potential data targets also receive the event. +type InitiateEvent struct{} + +func (InitiateEvent) ImplementsEvent() {} + +// CancelEvent is sent to data sources and targets to cancel the +// effects of an InitiateEvent. +type CancelEvent struct{} + +func (CancelEvent) ImplementsEvent() {} + +// DataEvent is sent to the target receiving the transfer. +type DataEvent struct { + // Type is the MIME type of Data. + Type string + // Open returns the transfer data. It is only valid to call Open in the frame + // the DataEvent is received. The caller must close the return value after use. + Open func() io.ReadCloser +} + +func (DataEvent) ImplementsEvent() {} diff --git a/vendor/gioui.org/layout/context.go b/vendor/gioui.org/layout/context.go index f47233b..5d31496 100644 --- a/vendor/gioui.org/layout/context.go +++ b/vendor/gioui.org/layout/context.go @@ -5,6 +5,7 @@ package layout import ( "time" + "gioui.org/f32" "gioui.org/io/event" "gioui.org/io/system" "gioui.org/op" @@ -39,15 +40,30 @@ type Context struct { // Constraints: Exact(e.Size), // } // -// NewContext calls ops.Reset. +// NewContext calls ops.Reset and adjusts ops for e.Insets. func NewContext(ops *op.Ops, e system.FrameEvent) Context { ops.Reset() + + size := e.Size + + if e.Insets != (system.Insets{}) { + left := e.Metric.Px(e.Insets.Left) + top := e.Metric.Px(e.Insets.Top) + op.Offset(f32.Point{ + X: float32(left), + Y: float32(top), + }).Add(ops) + + size.X -= left + e.Metric.Px(e.Insets.Right) + size.Y -= top + e.Metric.Px(e.Insets.Bottom) + } + return Context{ Ops: ops, Now: e.Now, Queue: e.Queue, Metric: e.Metric, - Constraints: Exact(e.Size), + Constraints: Exact(size), } } diff --git a/vendor/gioui.org/layout/doc.go b/vendor/gioui.org/layout/doc.go index d318765..3824084 100644 --- a/vendor/gioui.org/layout/doc.go +++ b/vendor/gioui.org/layout/doc.go @@ -37,7 +37,7 @@ This example both aligns and insets a child: inset := layout.Inset{...} inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions { - align := layout.Align(...) + align := layout.Alignment(...) return align.Layout(gtx, func(gtx layout.Context) layout.Dimensions { return widget.Layout(gtx, ...) }) diff --git a/vendor/gioui.org/layout/flex.go b/vendor/gioui.org/layout/flex.go index fe85b64..7d66f8f 100644 --- a/vendor/gioui.org/layout/flex.go +++ b/vendor/gioui.org/layout/flex.go @@ -65,9 +65,10 @@ func Rigid(widget Widget) FlexChild { } } -// Flexed returns a Flex child forced to take up w fraction of the -// of the space left over from Rigid children. The fraction is weight -// divided by the weight sum of all Flexed children. +// Flexed returns a Flex child forced to take up weight fraction of the +// space left over from Rigid children. The fraction is weight +// divided by either the weight sum of all Flexed children or the Flex +// WeightSum if non zero. func Flexed(weight float32, widget Widget) FlexChild { return FlexChild{ flex: true, @@ -82,10 +83,11 @@ func Flexed(weight float32, widget Widget) FlexChild { func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions { size := 0 cs := gtx.Constraints - mainMin, mainMax := axisMainConstraint(f.Axis, cs) - crossMin, crossMax := axisCrossConstraint(f.Axis, cs) + mainMin, mainMax := f.Axis.mainConstraint(cs) + crossMin, crossMax := f.Axis.crossConstraint(cs) remaining := mainMax var totalWeight float32 + cgtx := gtx // Lay out Rigid children. for i, child := range children { if child.flex { @@ -93,11 +95,10 @@ func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions { continue } macro := op.Record(gtx.Ops) - gtx := gtx - gtx.Constraints = axisConstraints(f.Axis, 0, remaining, crossMin, crossMax) - dims := child.widget(gtx) + cgtx.Constraints = f.Axis.constraints(0, remaining, crossMin, crossMax) + dims := child.widget(cgtx) c := macro.Stop() - sz := axisMain(f.Axis, dims.Size) + sz := f.Axis.Convert(dims.Size).X size += sz remaining -= sz if remaining < 0 { @@ -129,11 +130,10 @@ func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions { } } macro := op.Record(gtx.Ops) - gtx := gtx - gtx.Constraints = axisConstraints(f.Axis, flexSize, flexSize, crossMin, crossMax) - dims := child.widget(gtx) + cgtx.Constraints = f.Axis.constraints(flexSize, flexSize, crossMin, crossMax) + dims := child.widget(cgtx) c := macro.Stop() - sz := axisMain(f.Axis, dims.Size) + sz := f.Axis.Convert(dims.Size).X size += sz remaining -= sz if remaining < 0 { @@ -145,7 +145,7 @@ func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions { var maxCross int var maxBaseline int for _, child := range children { - if c := axisCross(f.Axis, child.dims.Size); c > maxCross { + if c := f.Axis.Convert(child.dims.Size).Y; c > maxCross { maxCross = c } if b := child.dims.Size.Y - child.dims.Baseline; b > maxBaseline { @@ -165,7 +165,9 @@ func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions { case SpaceEvenly: mainSize += space / (1 + len(children)) case SpaceAround: - mainSize += space / (len(children) * 2) + if len(children) > 0 { + mainSize += space / (len(children) * 2) + } } for i, child := range children { dims := child.dims @@ -173,27 +175,31 @@ func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions { var cross int switch f.Alignment { case End: - cross = maxCross - axisCross(f.Axis, dims.Size) + cross = maxCross - f.Axis.Convert(dims.Size).Y case Middle: - cross = (maxCross - axisCross(f.Axis, dims.Size)) / 2 + cross = (maxCross - f.Axis.Convert(dims.Size).Y) / 2 case Baseline: if f.Axis == Horizontal { cross = maxBaseline - b } } - stack := op.Push(gtx.Ops) - op.Offset(FPt(axisPoint(f.Axis, mainSize, cross))).Add(gtx.Ops) + pt := f.Axis.Convert(image.Pt(mainSize, cross)) + trans := op.Offset(FPt(pt)).Push(gtx.Ops) child.call.Add(gtx.Ops) - stack.Pop() - mainSize += axisMain(f.Axis, dims.Size) + trans.Pop() + mainSize += f.Axis.Convert(dims.Size).X if i < len(children)-1 { switch f.Spacing { case SpaceEvenly: mainSize += space / (1 + len(children)) case SpaceAround: - mainSize += space / len(children) + if len(children) > 0 { + mainSize += space / len(children) + } case SpaceBetween: - mainSize += space / (len(children) - 1) + if len(children) > 1 { + mainSize += space / (len(children) - 1) + } } } } @@ -205,60 +211,14 @@ func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions { case SpaceEvenly: mainSize += space / (1 + len(children)) case SpaceAround: - mainSize += space / (len(children) * 2) + if len(children) > 0 { + mainSize += space / (len(children) * 2) + } } - sz := axisPoint(f.Axis, mainSize, maxCross) + sz := f.Axis.Convert(image.Pt(mainSize, maxCross)) return Dimensions{Size: sz, Baseline: sz.Y - maxBaseline} } -func axisPoint(a Axis, main, cross int) image.Point { - if a == Horizontal { - return image.Point{main, cross} - } else { - return image.Point{cross, main} - } -} - -func axisMain(a Axis, sz image.Point) int { - if a == Horizontal { - return sz.X - } else { - return sz.Y - } -} - -func axisCross(a Axis, sz image.Point) int { - if a == Horizontal { - return sz.Y - } else { - return sz.X - } -} - -func axisMainConstraint(a Axis, cs Constraints) (int, int) { - if a == Horizontal { - return cs.Min.X, cs.Max.X - } else { - return cs.Min.Y, cs.Max.Y - } -} - -func axisCrossConstraint(a Axis, cs Constraints) (int, int) { - if a == Horizontal { - return cs.Min.Y, cs.Max.Y - } else { - return cs.Min.X, cs.Max.X - } -} - -func axisConstraints(a Axis, mainMin, mainMax, crossMin, crossMax int) Constraints { - if a == Horizontal { - return Constraints{Min: image.Pt(mainMin, crossMin), Max: image.Pt(mainMax, crossMax)} - } else { - return Constraints{Min: image.Pt(crossMin, mainMin), Max: image.Pt(crossMax, mainMax)} - } -} - func (s Spacing) String() string { switch s { case SpaceEnd: diff --git a/vendor/gioui.org/layout/layout.go b/vendor/gioui.org/layout/layout.go index 112c45d..2318c22 100644 --- a/vendor/gioui.org/layout/layout.go +++ b/vendor/gioui.org/layout/layout.go @@ -23,6 +23,10 @@ type Constraints struct { } // Dimensions are the resolved size and baseline for a widget. +// +// Baseline is the distance from the bottom of a widget to the baseline of +// any text it contains (or 0). The purpose is to be able to align text +// that span multiple widgets. type Dimensions struct { Size image.Point Baseline int @@ -105,7 +109,9 @@ func (c Constraints) Constrain(size image.Point) image.Point { return size } -// Inset adds space around a widget. +// Inset adds space around a widget by decreasing its maximum +// constraints. The minimum constraints will be adjusted to ensure +// they do not exceed the maximum. type Inset struct { Top, Right, Bottom, Left unit.Value } @@ -135,11 +141,10 @@ func (in Inset) Layout(gtx Context, w Widget) Dimensions { if mcs.Min.Y > mcs.Max.Y { mcs.Min.Y = mcs.Max.Y } - stack := op.Push(gtx.Ops) - op.Offset(FPt(image.Point{X: left, Y: top})).Add(gtx.Ops) gtx.Constraints = mcs + trans := op.Offset(FPt(image.Point{X: left, Y: top})).Push(gtx.Ops) dims := w(gtx) - stack.Pop() + trans.Pop() return Dimensions{ Size: dims.Size.Add(image.Point{X: right + left, Y: top + bottom}), Baseline: dims.Baseline + bottom, @@ -153,39 +158,70 @@ func UniformInset(v unit.Value) Inset { } // Layout a widget according to the direction. -func (a Direction) Layout(gtx Context, w Widget) Dimensions { +// The widget is called with the context constraints minimum cleared. +func (d Direction) Layout(gtx Context, w Widget) Dimensions { macro := op.Record(gtx.Ops) - cs := gtx.Constraints - gtx.Constraints.Min = image.Point{} + csn := gtx.Constraints.Min + switch d { + case N, S: + gtx.Constraints.Min.Y = 0 + case E, W: + gtx.Constraints.Min.X = 0 + default: + gtx.Constraints.Min = image.Point{} + } dims := w(gtx) call := macro.Stop() sz := dims.Size - if sz.X < cs.Min.X { - sz.X = cs.Min.X + if sz.X < csn.X { + sz.X = csn.X + } + if sz.Y < csn.Y { + sz.Y = csn.Y } - if sz.Y < cs.Min.Y { - sz.Y = cs.Min.Y + + p := d.Position(dims.Size, sz) + defer op.Offset(FPt(p)).Push(gtx.Ops).Pop() + call.Add(gtx.Ops) + + return Dimensions{ + Size: sz, + Baseline: dims.Baseline + sz.Y - dims.Size.Y - p.Y, } +} + +// Position calculates widget position according to the direction. +func (d Direction) Position(widget, bounds image.Point) image.Point { var p image.Point - switch Direction(a) { + + switch d { case N, S, Center: - p.X = (sz.X - dims.Size.X) / 2 + p.X = (bounds.X - widget.X) / 2 case NE, SE, E: - p.X = sz.X - dims.Size.X + p.X = bounds.X - widget.X } - switch Direction(a) { + + switch d { case W, Center, E: - p.Y = (sz.Y - dims.Size.Y) / 2 + p.Y = (bounds.Y - widget.Y) / 2 case SW, S, SE: - p.Y = sz.Y - dims.Size.Y + p.Y = bounds.Y - widget.Y } - stack := op.Push(gtx.Ops) - op.Offset(FPt(p)).Add(gtx.Ops) - call.Add(gtx.Ops) - stack.Pop() + + return p +} + +// Spacer adds space between widgets. +type Spacer struct { + Width, Height unit.Value +} + +func (s Spacer) Layout(gtx Context) Dimensions { return Dimensions{ - Size: sz, - Baseline: dims.Baseline + sz.Y - dims.Size.Y - p.Y, + Size: image.Point{ + X: gtx.Px(s.Width), + Y: gtx.Px(s.Height), + }, } } @@ -204,6 +240,50 @@ func (a Alignment) String() string { } } +// Convert a point in (x, y) coordinates to (main, cross) coordinates, +// or vice versa. Specifically, Convert((x, y)) returns (x, y) unchanged +// for the horizontal axis, or (y, x) for the vertical axis. +func (a Axis) Convert(pt image.Point) image.Point { + if a == Horizontal { + return pt + } + return image.Pt(pt.Y, pt.X) +} + +// FConvert a point in (x, y) coordinates to (main, cross) coordinates, +// or vice versa. Specifically, FConvert((x, y)) returns (x, y) unchanged +// for the horizontal axis, or (y, x) for the vertical axis. +func (a Axis) FConvert(pt f32.Point) f32.Point { + if a == Horizontal { + return pt + } + return f32.Pt(pt.Y, pt.X) +} + +// mainConstraint returns the min and max main constraints for axis a. +func (a Axis) mainConstraint(cs Constraints) (int, int) { + if a == Horizontal { + return cs.Min.X, cs.Max.X + } + return cs.Min.Y, cs.Max.Y +} + +// crossConstraint returns the min and max cross constraints for axis a. +func (a Axis) crossConstraint(cs Constraints) (int, int) { + if a == Horizontal { + return cs.Min.Y, cs.Max.Y + } + return cs.Min.X, cs.Max.X +} + +// constraints returns the constraints for axis a. +func (a Axis) constraints(mainMin, mainMax, crossMin, crossMax int) Constraints { + if a == Horizontal { + return Constraints{Min: image.Pt(mainMin, crossMin), Max: image.Pt(mainMax, crossMax)} + } + return Constraints{Min: image.Pt(crossMin, mainMin), Max: image.Pt(crossMax, mainMax)} +} + func (a Axis) String() string { switch a { case Horizontal: diff --git a/vendor/gioui.org/layout/list.go b/vendor/gioui.org/layout/list.go index 68b63f7..aa78f1b 100644 --- a/vendor/gioui.org/layout/list.go +++ b/vendor/gioui.org/layout/list.go @@ -6,7 +6,6 @@ import ( "image" "gioui.org/gesture" - "gioui.org/io/pointer" "gioui.org/op" "gioui.org/op/clip" ) @@ -29,15 +28,13 @@ type List struct { // Alignment is the cross axis alignment of list elements. Alignment Alignment - ctx Context - macro op.MacroOp - child op.MacroOp + cs Constraints scroll gesture.Scroll scrollDelta int // Position is updated during Layout. To save the list scroll position, // just save Position after Layout finishes. To scroll the list - // programatically, update Position (e.g. restore it from a saved value) + // programmatically, update Position (e.g. restore it from a saved value) // before calling Layout. Position Position @@ -71,6 +68,13 @@ type Position struct { // Offset is the distance in pixels from the top edge to the child at index // First. Offset int + // OffsetLast is the signed distance in pixels from the bottom edge to the + // bottom edge of the child at index First+Count. + OffsetLast int + // Count is the number of visible children. + Count int + // Length is the estimated total size of all children, measured in pixels. + Length int } const ( @@ -86,29 +90,41 @@ func (l *List) init(gtx Context, len int) { if l.more() { panic("unfinished child") } - l.ctx = gtx + l.cs = gtx.Constraints l.maxSize = 0 l.children = l.children[:0] l.len = len - l.update() + l.update(gtx) if l.scrollToEnd() || l.Position.First > len { l.Position.Offset = 0 l.Position.First = len } - l.macro = op.Record(gtx.Ops) - l.next() } // Layout the List. func (l *List) Layout(gtx Context, len int, w ListElement) Dimensions { - for l.init(gtx, len); l.more(); l.next() { - crossMin, crossMax := axisCrossConstraint(l.Axis, l.ctx.Constraints) - cs := axisConstraints(l.Axis, 0, inf, crossMin, crossMax) - i := l.index() - gtx.Constraints = cs - l.end(w(gtx, i)) + l.init(gtx, len) + crossMin, crossMax := l.Axis.crossConstraint(gtx.Constraints) + gtx.Constraints = l.Axis.constraints(0, inf, crossMin, crossMax) + macro := op.Record(gtx.Ops) + laidOutTotalLength := 0 + numLaidOut := 0 + + for l.next(); l.more(); l.next() { + child := op.Record(gtx.Ops) + dims := w(gtx, l.index()) + call := child.Stop() + l.end(dims, call) + laidOutTotalLength += l.Axis.Convert(dims.Size).X + numLaidOut++ + } + + if numLaidOut > 0 { + l.Position.Length = laidOutTotalLength * len / numLaidOut + } else { + l.Position.Length = 0 } - return l.layout() + return l.layout(gtx.Ops, macro) } func (l *List) scrollToEnd() bool { @@ -120,8 +136,8 @@ func (l *List) Dragging() bool { return l.scroll.State() == gesture.StateDragging } -func (l *List) update() { - d := l.scroll.Scroll(l.ctx.Metric, l.ctx, l.ctx.Now, gesture.Axis(l.Axis)) +func (l *List) update(gtx Context) { + d := l.scroll.Scroll(gtx.Metric, gtx, gtx.Now, gesture.Axis(l.Axis)) l.scrollDelta = d l.Position.Offset += d } @@ -136,9 +152,6 @@ func (l *List) next() { l.Position.Offset += l.scrollDelta l.dir = l.nextDir() } - if l.more() { - l.child = op.Record(l.ctx.Ops) - } } // index is current child's position in the underlying list. @@ -159,7 +172,7 @@ func (l *List) more() bool { } func (l *List) nextDir() iterationDir { - _, vsize := axisMainConstraint(l.Axis, l.ctx.Constraints) + _, vsize := l.Axis.mainConstraint(l.cs) last := l.Position.First + len(l.children) // Clamp offset. if l.maxSize-l.Position.Offset < vsize && last == l.len { @@ -180,16 +193,17 @@ func (l *List) nextDir() iterationDir { } // End the current child by specifying its dimensions. -func (l *List) end(dims Dimensions) { - call := l.child.Stop() +func (l *List) end(dims Dimensions, call op.CallOp) { child := scrollChild{dims.Size, call} - mainSize := axisMain(l.Axis, child.size) + mainSize := l.Axis.Convert(child.size).X l.maxSize += mainSize switch l.dir { case iterateForward: l.children = append(l.children, child) case iterateBackward: - l.children = append([]scrollChild{child}, l.children...) + l.children = append(l.children, scrollChild{}) + copy(l.children[1:], l.children) + l.children[0] = child l.Position.First-- l.Position.Offset += mainSize default: @@ -199,17 +213,18 @@ func (l *List) end(dims Dimensions) { } // Layout the List and return its dimensions. -func (l *List) layout() Dimensions { +func (l *List) layout(ops *op.Ops, macro op.MacroOp) Dimensions { if l.more() { panic("unfinished child") } - mainMin, mainMax := axisMainConstraint(l.Axis, l.ctx.Constraints) + mainMin, mainMax := l.Axis.mainConstraint(l.cs) children := l.children // Skip invisible children for len(children) > 0 { sz := children[0].size - mainSize := axisMain(l.Axis, sz) - if l.Position.Offset <= mainSize { + mainSize := l.Axis.Convert(sz).X + if l.Position.Offset < mainSize { + // First child is partially visible. break } l.Position.First++ @@ -219,32 +234,33 @@ func (l *List) layout() Dimensions { size := -l.Position.Offset var maxCross int for i, child := range children { - sz := child.size - if c := axisCross(l.Axis, sz); c > maxCross { + sz := l.Axis.Convert(child.size) + if c := sz.Y; c > maxCross { maxCross = c } - size += axisMain(l.Axis, sz) + size += sz.X if size >= mainMax { children = children[:i+1] break } } - ops := l.ctx.Ops + l.Position.Count = len(children) + l.Position.OffsetLast = mainMax - size pos := -l.Position.Offset // ScrollToEnd lists are end aligned. - if space := mainMax - size; l.ScrollToEnd && space > 0 { + if space := l.Position.OffsetLast; l.ScrollToEnd && space > 0 { pos += space } for _, child := range children { - sz := child.size + sz := l.Axis.Convert(child.size) var cross int switch l.Alignment { case End: - cross = maxCross - axisCross(l.Axis, sz) + cross = maxCross - sz.Y case Middle: - cross = (maxCross - axisCross(l.Axis, sz)) / 2 + cross = (maxCross - sz.Y) / 2 } - childSize := axisMain(l.Axis, sz) + childSize := sz.X max := childSize + pos if max > mainMax { max = mainMax @@ -254,14 +270,15 @@ func (l *List) layout() Dimensions { min = 0 } r := image.Rectangle{ - Min: axisPoint(l.Axis, min, -inf), - Max: axisPoint(l.Axis, max, inf), + Min: l.Axis.Convert(image.Pt(min, -inf)), + Max: l.Axis.Convert(image.Pt(max, inf)), } - stack := op.Push(ops) - clip.Rect{Rect: FRect(r)}.Op(ops).Add(ops) - op.Offset(FPt(axisPoint(l.Axis, pos, cross))).Add(ops) + cl := clip.Rect(r).Push(ops) + pt := l.Axis.Convert(image.Pt(pos, cross)) + trans := op.Offset(FPt(pt)).Push(ops) child.call.Add(ops) - stack.Pop() + trans.Pop() + cl.Pop() pos += childSize } atStart := l.Position.First == 0 && l.Position.Offset <= 0 @@ -276,11 +293,33 @@ func (l *List) layout() Dimensions { if pos > mainMax { pos = mainMax } - dims := axisPoint(l.Axis, pos, maxCross) - call := l.macro.Stop() - defer op.Push(l.ctx.Ops).Pop() - pointer.Rect(image.Rectangle{Max: dims}).Add(ops) - l.scroll.Add(ops) + if crossMin, crossMax := l.Axis.crossConstraint(l.cs); maxCross < crossMin { + maxCross = crossMin + } else if maxCross > crossMax { + maxCross = crossMax + } + dims := l.Axis.Convert(image.Pt(pos, maxCross)) + call := macro.Stop() + defer clip.Rect(image.Rectangle{Max: dims}).Push(ops).Pop() + + var min, max int + if o := l.Position.Offset; o > 0 { + // Use the size of the invisible part as scroll boundary. + min = -o + } else if l.Position.First > 0 { + min = -inf + } + if o := l.Position.OffsetLast; o < 0 { + max = -o + } else if l.Position.First+l.Position.Count < l.len { + max = inf + } + scrollRange := image.Rectangle{ + Min: l.Axis.Convert(image.Pt(min, 0)), + Max: l.Axis.Convert(image.Pt(max, 0)), + } + l.scroll.Add(ops, scrollRange) + call.Add(ops) return Dimensions{Size: dims} } diff --git a/vendor/gioui.org/layout/stack.go b/vendor/gioui.org/layout/stack.go index 0de43df..fb8b8ac 100644 --- a/vendor/gioui.org/layout/stack.go +++ b/vendor/gioui.org/layout/stack.go @@ -50,14 +50,14 @@ func Expanded(w Widget) StackChild { func (s Stack) Layout(gtx Context, children ...StackChild) Dimensions { var maxSZ image.Point // First lay out Stacked children. + cgtx := gtx + cgtx.Constraints.Min = image.Point{} for i, w := range children { if w.expanded { continue } macro := op.Record(gtx.Ops) - gtx := gtx - gtx.Constraints.Min = image.Pt(0, 0) - dims := w.widget(gtx) + dims := w.widget(cgtx) call := macro.Stop() if w := dims.Size.X; w > maxSZ.X { maxSZ.X = w @@ -74,11 +74,8 @@ func (s Stack) Layout(gtx Context, children ...StackChild) Dimensions { continue } macro := op.Record(gtx.Ops) - gtx := gtx - gtx.Constraints = Constraints{ - Min: maxSZ, Max: gtx.Constraints.Max, - } - dims := w.widget(gtx) + cgtx.Constraints.Min = maxSZ + dims := w.widget(cgtx) call := macro.Stop() if w := dims.Size.X; w > maxSZ.X { maxSZ.X = w @@ -107,10 +104,9 @@ func (s Stack) Layout(gtx Context, children ...StackChild) Dimensions { case SW, S, SE: p.Y = maxSZ.Y - sz.Y } - stack := op.Push(gtx.Ops) - op.Offset(FPt(p)).Add(gtx.Ops) + trans := op.Offset(FPt(p)).Push(gtx.Ops) ch.call.Add(gtx.Ops) - stack.Pop() + trans.Pop() if baseline == 0 { if b := ch.dims.Baseline; b != 0 { baseline = b + maxSZ.Y - sz.Y - p.Y diff --git a/vendor/gioui.org/op/clip/clip.go b/vendor/gioui.org/op/clip/clip.go index 59554b5..5b4dad0 100644 --- a/vendor/gioui.org/op/clip/clip.go +++ b/vendor/gioui.org/op/clip/clip.go @@ -4,85 +4,240 @@ package clip import ( "encoding/binary" + "hash/maphash" "image" "math" "gioui.org/f32" - "gioui.org/internal/opconst" "gioui.org/internal/ops" + "gioui.org/internal/scene" + "gioui.org/internal/stroke" "gioui.org/op" ) +// Op represents a clip area. Op intersects the current clip area with +// itself. +type Op struct { + path PathSpec + + outline bool + width float32 +} + +// Stack represents an Op pushed on the clip stack. +type Stack struct { + ops *ops.Ops + id ops.StackID + macroID int +} + +var pathSeed maphash.Seed + +func init() { + pathSeed = maphash.MakeSeed() +} + +// Push saves the current clip state on the stack and updates the current +// state to the intersection of the current p. +func (p Op) Push(o *op.Ops) Stack { + id, macroID := ops.PushOp(&o.Internal, ops.ClipStack) + p.add(o) + return Stack{ops: &o.Internal, id: id, macroID: macroID} +} + +func (p Op) add(o *op.Ops) { + path := p.path + + bo := binary.LittleEndian + if path.hasSegments { + data := ops.Write(&o.Internal, ops.TypePathLen) + data[0] = byte(ops.TypePath) + bo.PutUint64(data[1:], path.hash) + path.spec.Add(o) + } + + bounds := path.bounds + if p.width > 0 { + // Expand bounds to cover stroke. + half := int(p.width*.5 + .5) + bounds.Min.X -= half + bounds.Min.Y -= half + bounds.Max.X += half + bounds.Max.Y += half + data := ops.Write(&o.Internal, ops.TypeStrokeLen) + data[0] = byte(ops.TypeStroke) + bo := binary.LittleEndian + bo.PutUint32(data[1:], math.Float32bits(p.width)) + } + + data := ops.Write(&o.Internal, ops.TypeClipLen) + data[0] = byte(ops.TypeClip) + bo.PutUint32(data[1:], uint32(bounds.Min.X)) + bo.PutUint32(data[5:], uint32(bounds.Min.Y)) + bo.PutUint32(data[9:], uint32(bounds.Max.X)) + bo.PutUint32(data[13:], uint32(bounds.Max.Y)) + if p.outline { + data[17] = byte(1) + } + data[18] = byte(path.shape) +} + +func (s Stack) Pop() { + ops.PopOp(s.ops, ops.ClipStack, s.id, s.macroID) + data := ops.Write(s.ops, ops.TypePopClipLen) + data[0] = byte(ops.TypePopClip) +} + +type PathSpec struct { + spec op.CallOp + // hasSegments tracks whether there are any segments in the path. + hasSegments bool + bounds image.Rectangle + shape ops.Shape + hash uint64 +} + // Path constructs a Op clip path described by lines and // Bézier curves, where drawing outside the Path is discarded. -// The inside-ness of a pixel is determines by the even-odd rule, +// The inside-ness of a pixel is determines by the non-zero winding rule, // similar to the SVG rule of the same name. // // Path generates no garbage and can be used for dynamic paths; path // data is stored directly in the Ops list supplied to Begin. type Path struct { - ops *op.Ops - contour int - pen f32.Point - macro op.MacroOp - start f32.Point + ops *ops.Ops + contour int + pen f32.Point + macro op.MacroOp + start f32.Point + hasSegments bool + bounds f32.Rectangle + hash maphash.Hash } -// Op sets the current clip to the intersection of -// the existing clip with this clip. -// -// If you need to reset the clip to its previous values after -// applying a Op, use op.StackOp. -type Op struct { - call op.CallOp - bounds f32.Rectangle +// Pos returns the current pen position. +func (p *Path) Pos() f32.Point { return p.pen } + +// Begin the path, storing the path data and final Op into ops. +func (p *Path) Begin(o *op.Ops) { + *p = Path{ + ops: &o.Internal, + macro: op.Record(o), + contour: 1, + } + p.hash.SetSeed(pathSeed) + data := ops.Write(p.ops, ops.TypeAuxLen) + data[0] = byte(ops.TypeAux) } -func (p Op) Add(o *op.Ops) { - p.call.Add(o) - data := o.Write(opconst.TypeClipLen) - data[0] = byte(opconst.TypeClip) - bo := binary.LittleEndian - bo.PutUint32(data[1:], math.Float32bits(p.bounds.Min.X)) - bo.PutUint32(data[5:], math.Float32bits(p.bounds.Min.Y)) - bo.PutUint32(data[9:], math.Float32bits(p.bounds.Max.X)) - bo.PutUint32(data[13:], math.Float32bits(p.bounds.Max.Y)) +// End returns a PathSpec ready to use in clipping operations. +func (p *Path) End() PathSpec { + p.gap() + c := p.macro.Stop() + return PathSpec{ + spec: c, + hasSegments: p.hasSegments, + bounds: boundRectF(p.bounds), + hash: p.hash.Sum64(), + } } -// Begin the path, storing the path data and final Op into ops. -func (p *Path) Begin(ops *op.Ops) { - p.ops = ops - p.macro = op.Record(ops) - // Write the TypeAux opcode - data := ops.Write(opconst.TypeAuxLen) - data[0] = byte(opconst.TypeAux) +// Move moves the pen by the amount specified by delta. +func (p *Path) Move(delta f32.Point) { + to := delta.Add(p.pen) + p.MoveTo(to) } -// MoveTo moves the pen to the given position. -func (p *Path) Move(to f32.Point) { - to = to.Add(p.pen) +// MoveTo moves the pen to the specified absolute coordinate. +func (p *Path) MoveTo(to f32.Point) { + if p.pen == to { + return + } + p.gap() p.end() p.pen = to p.start = to } -// end completes the current contour. -func (p *Path) end() { +func (p *Path) gap() { if p.pen != p.start { - p.lineTo(p.start) + // A closed contour starts and ends in the same point. + // This move creates a gap in the contour, register it. + data := ops.Write(p.ops, scene.CommandSize+4) + bo := binary.LittleEndian + bo.PutUint32(data[0:], uint32(p.contour)) + p.cmd(data[4:], scene.Gap(p.pen, p.start)) } +} + +// end completes the current contour. +func (p *Path) end() { p.contour++ } // Line moves the pen by the amount specified by delta, recording a line. func (p *Path) Line(delta f32.Point) { to := delta.Add(p.pen) - p.lineTo(to) + p.LineTo(to) } -func (p *Path) lineTo(to f32.Point) { - // Model lines as degenerate quadratic Béziers. - p.quadTo(to.Add(p.pen).Mul(.5), to) +// LineTo moves the pen to the absolute point specified, recording a line. +func (p *Path) LineTo(to f32.Point) { + data := ops.Write(p.ops, scene.CommandSize+4) + bo := binary.LittleEndian + bo.PutUint32(data[0:], uint32(p.contour)) + p.cmd(data[4:], scene.Line(p.pen, to)) + p.pen = to + p.expand(to) +} + +func (p *Path) cmd(data []byte, c scene.Command) { + ops.EncodeCommand(data, c) + p.hash.Write(data) +} + +func (p *Path) expand(pt f32.Point) { + if !p.hasSegments { + p.hasSegments = true + p.bounds = f32.Rectangle{Min: pt, Max: pt} + } else { + b := p.bounds + if pt.X < b.Min.X { + b.Min.X = pt.X + } + if pt.Y < b.Min.Y { + b.Min.Y = pt.Y + } + if pt.X > b.Max.X { + b.Max.X = pt.X + } + if pt.Y > b.Max.Y { + b.Max.Y = pt.Y + } + p.bounds = b + } +} + +// boundRectF returns a bounding image.Rectangle for a f32.Rectangle. +func boundRectF(r f32.Rectangle) image.Rectangle { + return image.Rectangle{ + Min: image.Point{ + X: int(floor(r.Min.X)), + Y: int(floor(r.Min.Y)), + }, + Max: image.Point{ + X: int(ceil(r.Max.X)), + Y: int(ceil(r.Max.Y)), + }, + } +} + +func ceil(v float32) int { + return int(math.Ceil(float64(v))) +} + +func floor(v float32) int { + return int(math.Floor(float64(v))) } // Quad records a quadratic Bézier from the pen to end @@ -90,174 +245,101 @@ func (p *Path) lineTo(to f32.Point) { func (p *Path) Quad(ctrl, to f32.Point) { ctrl = ctrl.Add(p.pen) to = to.Add(p.pen) - p.quadTo(ctrl, to) + p.QuadTo(ctrl, to) } -func (p *Path) quadTo(ctrl, to f32.Point) { - data := p.ops.Write(ops.QuadSize + 4) +// QuadTo records a quadratic Bézier from the pen to end +// with the control point ctrl, with absolute coordinates. +func (p *Path) QuadTo(ctrl, to f32.Point) { + data := ops.Write(p.ops, scene.CommandSize+4) bo := binary.LittleEndian bo.PutUint32(data[0:], uint32(p.contour)) - ops.EncodeQuad(data[4:], ops.Quad{ - From: p.pen, - Ctrl: ctrl, - To: to, - }) + p.cmd(data[4:], scene.Quad(p.pen, ctrl, to)) p.pen = to + p.expand(ctrl) + p.expand(to) +} + +// ArcTo adds an elliptical arc to the path. The implied ellipse is defined +// by its focus points f1 and f2. +// The arc starts in the current point and ends angle radians along the ellipse boundary. +// The sign of angle determines the direction; positive being counter-clockwise, +// negative clockwise. +func (p *Path) ArcTo(f1, f2 f32.Point, angle float32) { + const segments = 16 + m := stroke.ArcTransform(p.pen, f1, f2, angle, segments) + + for i := 0; i < segments; i++ { + p0 := p.pen + p1 := m.Transform(p0) + p2 := m.Transform(p1) + ctl := p1.Mul(2).Sub(p0.Add(p2).Mul(.5)) + p.QuadTo(ctl, p2) + } +} + +// Arc is like ArcTo where f1 and f2 are relative to the current position. +func (p *Path) Arc(f1, f2 f32.Point, angle float32) { + f1 = f1.Add(p.pen) + f2 = f2.Add(p.pen) + p.ArcTo(f1, f2, angle) } // Cube records a cubic Bézier from the pen through // two control points ending in to. func (p *Path) Cube(ctrl0, ctrl1, to f32.Point) { - ctrl0 = ctrl0.Add(p.pen) - ctrl1 = ctrl1.Add(p.pen) - to = to.Add(p.pen) - // Set the maximum distance proportionally to the longest side - // of the bounding rectangle. - hull := f32.Rectangle{ - Min: p.pen, - Max: ctrl0, - }.Canon().Add(ctrl1).Add(to) - l := hull.Dx() - if h := hull.Dy(); h > l { - l = h - } - p.approxCubeTo(0, l*0.001, ctrl0, ctrl1, to) -} - -// approxCube approximates a cubic Bézier by a series of quadratic -// curves. -func (p *Path) approxCubeTo(splits int, maxDist float32, ctrl0, ctrl1, to f32.Point) int { - // The idea is from - // https://caffeineowl.com/graphics/2d/vectorial/cubic2quad01.html - // where a quadratic approximates a cubic by eliminating its t³ term - // from its polynomial expression anchored at the starting point: - // - // P(t) = pen + 3t(ctrl0 - pen) + 3t²(ctrl1 - 2ctrl0 + pen) + t³(to - 3ctrl1 + 3ctrl0 - pen) - // - // The control point for the new quadratic Q1 that shares starting point, pen, with P is - // - // C1 = (3ctrl0 - pen)/2 - // - // The reverse cubic anchored at the end point has the polynomial - // - // P'(t) = to + 3t(ctrl1 - to) + 3t²(ctrl0 - 2ctrl1 + to) + t³(pen - 3ctrl0 + 3ctrl1 - to) - // - // The corresponding quadratic Q2 that shares the end point, to, with P has control - // point - // - // C2 = (3ctrl1 - to)/2 - // - // The combined quadratic Bézier, Q, shares both start and end points with its cubic - // and use the midpoint between the two curves Q1 and Q2 as control point: - // - // C = (3ctrl0 - pen + 3ctrl1 - to)/4 - c := ctrl0.Mul(3).Sub(p.pen).Add(ctrl1.Mul(3)).Sub(to).Mul(1.0 / 4.0) - const maxSplits = 32 - if splits >= maxSplits { - p.quadTo(c, to) - return splits + p.CubeTo(p.pen.Add(ctrl0), p.pen.Add(ctrl1), p.pen.Add(to)) +} + +// CubeTo records a cubic Bézier from the pen through +// two control points ending in to, with absolute coordinates. +func (p *Path) CubeTo(ctrl0, ctrl1, to f32.Point) { + if ctrl0 == p.pen && ctrl1 == p.pen && to == p.pen { + return } - // The maximum distance between the cubic P and its approximation Q given t - // can be shown to be - // - // d = sqrt(3)/36*|to - 3ctrl1 + 3ctrl0 - pen| - // - // To save a square root, compare d² with the squared tolerance. - v := to.Sub(ctrl1.Mul(3)).Add(ctrl0.Mul(3)).Sub(p.pen) - d2 := (v.X*v.X + v.Y*v.Y) * 3 / (36 * 36) - if d2 <= maxDist*maxDist { - p.quadTo(c, to) - return splits + data := ops.Write(p.ops, scene.CommandSize+4) + bo := binary.LittleEndian + bo.PutUint32(data[0:], uint32(p.contour)) + p.cmd(data[4:], scene.Cubic(p.pen, ctrl0, ctrl1, to)) + p.pen = to + p.expand(ctrl0) + p.expand(ctrl1) + p.expand(to) +} + +// Close closes the path contour. +func (p *Path) Close() { + if p.pen != p.start { + p.LineTo(p.start) } - // De Casteljau split the curve and approximate the halves. - t := float32(0.5) - c0 := p.pen.Add(ctrl0.Sub(p.pen).Mul(t)) - c1 := ctrl0.Add(ctrl1.Sub(ctrl0).Mul(t)) - c2 := ctrl1.Add(to.Sub(ctrl1).Mul(t)) - c01 := c0.Add(c1.Sub(c0).Mul(t)) - c12 := c1.Add(c2.Sub(c1).Mul(t)) - c0112 := c01.Add(c12.Sub(c01).Mul(t)) - splits++ - splits = p.approxCubeTo(splits, maxDist, c0, c01, c0112) - splits = p.approxCubeTo(splits, maxDist, c12, c2, to) - return splits -} - -// End the path and return a clip operation that represents it. -func (p *Path) End() Op { p.end() - c := p.macro.Stop() +} + +// Stroke represents a stroked path. +type Stroke struct { + Path PathSpec + // Width of the stroked path. + Width float32 +} + +// Op returns a clip operation representing the stroke. +func (s Stroke) Op() Op { return Op{ - call: c, + path: s.Path, + width: s.Width, } } -// Rect represents the clip area of a rectangle with rounded -// corners.The origin is in the upper left -// corner. -// Specify a square with corner radii equal to half the square size to -// construct a circular clip area. -type Rect struct { - Rect f32.Rectangle - // The corner radii. - SE, SW, NW, NE float32 -} - -// Op returns the Op for the rectangle. -func (rr Rect) Op(ops *op.Ops) Op { - r := rr.Rect - // Optimize for the common pixel aligned rectangle with no - // corner rounding. - if rr.SE == 0 && rr.SW == 0 && rr.NW == 0 && rr.NE == 0 { - ri := image.Rectangle{ - Min: image.Point{X: int(r.Min.X), Y: int(r.Min.Y)}, - Max: image.Point{X: int(r.Max.X), Y: int(r.Max.Y)}, - } - // Optimize pixel-aligned rectangles to just its bounds. - if r == fRect(ri) { - return Op{bounds: r} - } - } - return roundRect(ops, r, rr.SE, rr.SW, rr.NW, rr.NE) -} - -// Add is a shorthand for Op(ops).Add(ops). -func (rr Rect) Add(ops *op.Ops) { - rr.Op(ops).Add(ops) -} - -// roundRect returns the clip area of a rectangle with rounded -// corners defined by their radii. -func roundRect(ops *op.Ops, r f32.Rectangle, se, sw, nw, ne float32) Op { - size := r.Size() - // https://pomax.github.io/bezierinfo/#circles_cubic. - w, h := float32(size.X), float32(size.Y) - const c = 0.55228475 // 4*(sqrt(2)-1)/3 - var p Path - p.Begin(ops) - p.Move(r.Min) - - p.Move(f32.Point{X: w, Y: h - se}) - p.Cube(f32.Point{X: 0, Y: se * c}, f32.Point{X: -se + se*c, Y: se}, f32.Point{X: -se, Y: se}) // SE - p.Line(f32.Point{X: sw - w + se, Y: 0}) - p.Cube(f32.Point{X: -sw * c, Y: 0}, f32.Point{X: -sw, Y: -sw + sw*c}, f32.Point{X: -sw, Y: -sw}) // SW - p.Line(f32.Point{X: 0, Y: nw - h + sw}) - p.Cube(f32.Point{X: 0, Y: -nw * c}, f32.Point{X: nw - nw*c, Y: -nw}, f32.Point{X: nw, Y: -nw}) // NW - p.Line(f32.Point{X: w - ne - nw, Y: 0}) - p.Cube(f32.Point{X: ne * c, Y: 0}, f32.Point{X: ne, Y: ne - ne*c}, f32.Point{X: ne, Y: ne}) // NE - return p.End() -} - -// fRect converts a rectangle to a f32.Rectangle. -func fRect(r image.Rectangle) f32.Rectangle { - return f32.Rectangle{ - Min: fPt(r.Min), Max: fPt(r.Max), - } +// Outline represents the area inside of a path, according to the +// non-zero winding rule. +type Outline struct { + Path PathSpec } -// fPt converts an point to a f32.Point. -func fPt(p image.Point) f32.Point { - return f32.Point{ - X: float32(p.X), Y: float32(p.Y), +// Op returns a clip operation representing the outline. +func (o Outline) Op() Op { + return Op{ + path: o.Path, + outline: true, } } diff --git a/vendor/gioui.org/op/clip/doc.go b/vendor/gioui.org/op/clip/doc.go index 6ba5546..894cffd 100644 --- a/vendor/gioui.org/op/clip/doc.go +++ b/vendor/gioui.org/op/clip/doc.go @@ -1,16 +1,14 @@ // SPDX-License-Identifier: Unlicense OR MIT /* -Package clip provides operations for clipping paint operations. -Drawing outside the current clip area is ignored. +Package clip provides operations for defining areas that applies to operations +such as paints and pointer handlers. -The current clip is initially the infinite set. An Op sets the clip -to the intersection of the current clip and the clip area it -represents. If you need to reset the current clip to its value -before applying an Op, use op.StackOp. +The current clip is initially the infinite set. Pushing an Op sets the clip +to the intersection of the current clip and pushed clip area. Popping the +area restores the clip to its state before pushing. -General clipping areas are constructed with Path. Simpler special -cases such as rectangular clip areas also exist as convenient -constructors. +General clipping areas are constructed with Path. Common cases such as +rectangular clip areas also exist as convenient constructors. */ package clip diff --git a/vendor/gioui.org/op/clip/shapes.go b/vendor/gioui.org/op/clip/shapes.go new file mode 100644 index 0000000..30609e5 --- /dev/null +++ b/vendor/gioui.org/op/clip/shapes.go @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package clip + +import ( + "image" + "math" + + "gioui.org/f32" + "gioui.org/internal/ops" + "gioui.org/op" +) + +// Rect represents the clip area of a pixel-aligned rectangle. +type Rect image.Rectangle + +// Op returns the op for the rectangle. +func (r Rect) Op() Op { + return Op{ + outline: true, + path: r.Path(), + } +} + +// Push the clip operation on the clip stack. +func (r Rect) Push(ops *op.Ops) Stack { + return r.Op().Push(ops) +} + +// Path returns the PathSpec for the rectangle. +func (r Rect) Path() PathSpec { + return PathSpec{ + shape: ops.Rect, + bounds: image.Rectangle(r), + } +} + +// UniformRRect returns an RRect with all corner radii set to the +// provided radius. +func UniformRRect(rect f32.Rectangle, radius float32) RRect { + return RRect{ + Rect: rect, + SE: radius, + SW: radius, + NE: radius, + NW: radius, + } +} + +// RRect represents the clip area of a rectangle with rounded +// corners. +// +// Specify a square with corner radii equal to half the square size to +// construct a circular clip area. +type RRect struct { + Rect f32.Rectangle + // The corner radii. + SE, SW, NW, NE float32 +} + +// Op returns the op for the rounded rectangle. +func (rr RRect) Op(ops *op.Ops) Op { + if rr.SE == 0 && rr.SW == 0 && rr.NW == 0 && rr.NE == 0 { + r := image.Rectangle{ + Min: image.Point{X: int(rr.Rect.Min.X), Y: int(rr.Rect.Min.Y)}, + Max: image.Point{X: int(rr.Rect.Max.X), Y: int(rr.Rect.Max.Y)}, + } + // Only use Rect if rr is pixel-aligned, as Rect is guaranteed to be. + if fPt(r.Min) == rr.Rect.Min && fPt(r.Max) == rr.Rect.Max { + return Rect(r).Op() + } + } + return Outline{Path: rr.Path(ops)}.Op() +} + +// Push the rectangle clip on the clip stack. +func (rr RRect) Push(ops *op.Ops) Stack { + return rr.Op(ops).Push(ops) +} + +// Path returns the PathSpec for the rounded rectangle. +func (rr RRect) Path(ops *op.Ops) PathSpec { + var p Path + p.Begin(ops) + + // https://pomax.github.io/bezierinfo/#circles_cubic. + const q = 4 * (math.Sqrt2 - 1) / 3 + const iq = 1 - q + + se, sw, nw, ne := rr.SE, rr.SW, rr.NW, rr.NE + w, n, e, s := rr.Rect.Min.X, rr.Rect.Min.Y, rr.Rect.Max.X, rr.Rect.Max.Y + + p.MoveTo(f32.Point{X: w + nw, Y: n}) + p.LineTo(f32.Point{X: e - ne, Y: n}) // N + p.CubeTo( // NE + f32.Point{X: e - ne*iq, Y: n}, + f32.Point{X: e, Y: n + ne*iq}, + f32.Point{X: e, Y: n + ne}) + p.LineTo(f32.Point{X: e, Y: s - se}) // E + p.CubeTo( // SE + f32.Point{X: e, Y: s - se*iq}, + f32.Point{X: e - se*iq, Y: s}, + f32.Point{X: e - se, Y: s}) + p.LineTo(f32.Point{X: w + sw, Y: s}) // S + p.CubeTo( // SW + f32.Point{X: w + sw*iq, Y: s}, + f32.Point{X: w, Y: s - sw*iq}, + f32.Point{X: w, Y: s - sw}) + p.LineTo(f32.Point{X: w, Y: n + nw}) // W + p.CubeTo( // NW + f32.Point{X: w, Y: n + nw*iq}, + f32.Point{X: w + nw*iq, Y: n}, + f32.Point{X: w + nw, Y: n}) + + return p.End() +} + +// Ellipse represents the largest axis-aligned ellipse that +// is contained in its bounds. +type Ellipse f32.Rectangle + +// Op returns the op for the filled ellipse. +func (e Ellipse) Op(ops *op.Ops) Op { + return Outline{Path: e.Path(ops)}.Op() +} + +// Push the filled ellipse clip op on the clip stack. +func (e Ellipse) Push(ops *op.Ops) Stack { + return e.Op(ops).Push(ops) +} + +// Path constructs a path for the ellipse. +func (e Ellipse) Path(o *op.Ops) PathSpec { + bounds := f32.Rectangle(e) + if bounds.Dx() == 0 || bounds.Dy() == 0 { + return PathSpec{shape: ops.Rect} + } + + var p Path + p.Begin(o) + + center := bounds.Max.Add(bounds.Min).Mul(.5) + diam := bounds.Dx() + r := diam * .5 + // We'll model the ellipse as a circle scaled in the Y + // direction. + scale := bounds.Dy() / diam + + // https://pomax.github.io/bezierinfo/#circles_cubic. + const q = 4 * (math.Sqrt2 - 1) / 3 + + curve := r * q + top := f32.Point{X: center.X, Y: center.Y - r*scale} + + p.MoveTo(top) + p.CubeTo( + f32.Point{X: center.X + curve, Y: center.Y - r*scale}, + f32.Point{X: center.X + r, Y: center.Y - curve*scale}, + f32.Point{X: center.X + r, Y: center.Y}, + ) + p.CubeTo( + f32.Point{X: center.X + r, Y: center.Y + curve*scale}, + f32.Point{X: center.X + curve, Y: center.Y + r*scale}, + f32.Point{X: center.X, Y: center.Y + r*scale}, + ) + p.CubeTo( + f32.Point{X: center.X - curve, Y: center.Y + r*scale}, + f32.Point{X: center.X - r, Y: center.Y + curve*scale}, + f32.Point{X: center.X - r, Y: center.Y}, + ) + p.CubeTo( + f32.Point{X: center.X - r, Y: center.Y - curve*scale}, + f32.Point{X: center.X - curve, Y: center.Y - r*scale}, + top, + ) + ellipse := p.End() + ellipse.shape = ops.Ellipse + return ellipse +} + +func fPt(p image.Point) f32.Point { + return f32.Point{ + X: float32(p.X), Y: float32(p.Y), + } +} diff --git a/vendor/gioui.org/op/op.go b/vendor/gioui.org/op/op.go index 174d318..f0b2620 100644 --- a/vendor/gioui.org/op/op.go +++ b/vendor/gioui.org/op/op.go @@ -30,25 +30,25 @@ Drawing a colored square: State -An Ops list can be viewed as a very simple virtual machine: it has an implicit -mutable state stack and execution flow can be controlled with macros. +An Ops list can be viewed as a very simple virtual machine: it has state such +as transformation and color and execution flow can be controlled with macros. -The StackOp saves the current state to the state stack and restores it later: +Some state, such as the current color, is modified directly by operations with +Add methods. Other state, such as transformation and clip shape, are +represented by stacks. + +This example sets the simple color state and pushes an offset to the +transformation stack. ops := new(op.Ops) - // Save the current state, in particular the transform. - stack := op.Push(ops) - // Apply a transform to subsequent operations. - op.Offset(...).Add(ops) + // Set the color. + paint.ColorOp{...}.Add(ops) + // Apply an offset to subsequent operations. + stack := op.Offset(...).Push(ops) ... - // Restore the previous transform. + // Undo the offset transformation. stack.Pop() -You can also use this one-line to save the current state and restore it at the -end of a function : - - defer op.Push(ops).Pop() - The MacroOp records a list of operations to be executed later: ops := new(op.Ops) @@ -71,44 +71,29 @@ import ( "time" "gioui.org/f32" - "gioui.org/internal/opconst" + "gioui.org/internal/ops" ) // Ops holds a list of operations. Operations are stored in // serialized form to avoid garbage during construction of // the ops list. type Ops struct { - // version is incremented at each Reset. - version int - // data contains the serialized operations. - data []byte - // External references for operations. - refs []interface{} - - stackStack stack - macroStack stack -} - -// StackOp saves and restores the operation state -// in a stack-like manner. -type StackOp struct { - id stackID - macroID int - ops *Ops + // Internal is for internal use, despite being exported. + Internal ops.Ops } // MacroOp records a list of operations for later use. type MacroOp struct { - ops *Ops - id stackID - pc pc + ops *ops.Ops + id ops.StackID + pc ops.PC } // CallOp invokes the operations recorded by Record. type CallOp struct { // Ops is the list of operations to invoke. - ops *Ops - pc pc + ops *ops.Ops + pc ops.PC } // InvalidateOp requests a redraw at the given time. Use @@ -117,100 +102,58 @@ type InvalidateOp struct { At time.Time } -// TransformOp applies a transform to the current transform. The zero value -// for TransformOp represents the identity transform. +// TransformOp represents a transformation that can be pushed on the +// transformation stack. type TransformOp struct { t f32.Affine2D } -// stack tracks the integer identities of StackOp and MacroOp -// operations to ensure correct pairing of Push/Pop and Record/End. -type stack struct { - currentID int - nextID int -} - -type stackID struct { - id int - prev int -} - -type pc struct { - data int - refs int -} - -// Push (save) the current operations state. -func Push(o *Ops) StackOp { - s := StackOp{ - ops: o, - id: o.stackStack.push(), - macroID: o.macroStack.currentID, - } - data := o.Write(opconst.TypePushLen) - data[0] = byte(opconst.TypePush) - return s +// TransformStack represents a TransformOp pushed on the transformation stack. +type TransformStack struct { + id ops.StackID + macroID int + ops *ops.Ops } -// Pop (restore) a previously Pushed operations state. -func (s StackOp) Pop() { - if s.ops.macroStack.currentID != s.macroID { - panic("pop in a different macro than push") +// Defer executes c after all other operations have completed, including +// previously deferred operations. +// Defer saves the transformation stack and pushes it prior to executing +// c. All other operation state is reset. +// +// Note that deferred operations are executed in first-in-first-out order, +// unlike the Go facility of the same name. +func Defer(o *Ops, c CallOp) { + if c.ops == nil { + return } - s.ops.stackStack.pop(s.id) - data := s.ops.Write(opconst.TypePopLen) - data[0] = byte(opconst.TypePop) + state := ops.Save(&o.Internal) + // Wrap c in a macro that loads the saved state before execution. + m := Record(o) + state.Load() + c.Add(o) + c = m.Stop() + // A Defer is recorded as a TypeDefer followed by the + // wrapped macro. + data := ops.Write(&o.Internal, ops.TypeDeferLen) + data[0] = byte(ops.TypeDefer) + c.Add(o) } // Reset the Ops, preparing it for re-use. Reset invalidates // any recorded macros. func (o *Ops) Reset() { - o.stackStack = stack{} - o.macroStack = stack{} - // Leave references to the GC. - for i := range o.refs { - o.refs[i] = nil - } - o.data = o.data[:0] - o.refs = o.refs[:0] - o.version++ -} - -// Data is for internal use only. -func (o *Ops) Data() []byte { - return o.data -} - -// Refs is for internal use only. -func (o *Ops) Refs() []interface{} { - return o.refs -} - -// Version is for internal use only. -func (o *Ops) Version() int { - return o.version -} - -// Write is for internal use only. -func (o *Ops) Write(n int, refs ...interface{}) []byte { - o.data = append(o.data, make([]byte, n)...) - o.refs = append(o.refs, refs...) - return o.data[len(o.data)-n:] -} - -func (o *Ops) pc() pc { - return pc{data: len(o.data), refs: len(o.refs)} + ops.Reset(&o.Internal) } // Record a macro of operations. func Record(o *Ops) MacroOp { m := MacroOp{ - ops: o, - id: o.macroStack.push(), - pc: o.pc(), + ops: &o.Internal, + id: ops.PushMacro(&o.Internal), + pc: ops.PCFor(&o.Internal), } // Reserve room for a macro definition. Updated in Stop. - m.ops.Write(opconst.TypeMacroLen) + ops.Write(m.ops, ops.TypeMacroLen) m.fill() return m } @@ -218,7 +161,7 @@ func Record(o *Ops) MacroOp { // Stop ends a previously started recording and returns an // operation for replaying it. func (m MacroOp) Stop() CallOp { - m.ops.macroStack.pop(m.id) + ops.PopMacro(m.ops, m.id) m.fill() return CallOp{ ops: m.ops, @@ -227,14 +170,7 @@ func (m MacroOp) Stop() CallOp { } func (m MacroOp) fill() { - pc := m.ops.pc() - // Fill out the macro definition reserved in Record. - data := m.ops.data[m.pc.data:] - data = data[:opconst.TypeMacroLen] - data[0] = byte(opconst.TypeMacro) - bo := binary.LittleEndian - bo.PutUint32(data[1:], uint32(pc.data)) - bo.PutUint32(data[5:], uint32(pc.refs)) + ops.FillMacro(m.ops, m.pc) } // Add the recorded list of operations. Add @@ -244,16 +180,12 @@ func (c CallOp) Add(o *Ops) { if c.ops == nil { return } - data := o.Write(opconst.TypeCallLen, c.ops) - data[0] = byte(opconst.TypeCall) - bo := binary.LittleEndian - bo.PutUint32(data[1:], uint32(c.pc.data)) - bo.PutUint32(data[5:], uint32(c.pc.refs)) + ops.AddCall(&o.Internal, c.ops, c.pc) } func (r InvalidateOp) Add(o *Ops) { - data := o.Write(opconst.TypeRedrawLen) - data[0] = byte(opconst.TypeInvalidate) + data := ops.Write(&o.Internal, ops.TypeRedrawLen) + data[0] = byte(ops.TypeInvalidate) bo := binary.LittleEndian // UnixNano cannot represent the zero time. if t := r.At; !t.IsZero() { @@ -274,36 +206,38 @@ func Affine(a f32.Affine2D) TransformOp { return TransformOp{t: a} } -func (t TransformOp) Add(o *Ops) { - data := o.Write(opconst.TypeTransformLen) - data[0] = byte(opconst.TypeTransform) - bo := binary.LittleEndian - a, b, c, d, e, f := t.t.Elems() - bo.PutUint32(data[1:], math.Float32bits(a)) - bo.PutUint32(data[1+4*1:], math.Float32bits(b)) - bo.PutUint32(data[1+4*2:], math.Float32bits(c)) - bo.PutUint32(data[1+4*3:], math.Float32bits(d)) - bo.PutUint32(data[1+4*4:], math.Float32bits(e)) - bo.PutUint32(data[1+4*5:], math.Float32bits(f)) +// Push the current transformation to the stack and then multiply the +// current transformation with t. +func (t TransformOp) Push(o *Ops) TransformStack { + id, macroID := ops.PushOp(&o.Internal, ops.TransStack) + t.add(o, true) + return TransformStack{ops: &o.Internal, id: id, macroID: macroID} } -func (s *stack) push() stackID { - s.nextID++ - sid := stackID{ - id: s.nextID, - prev: s.currentID, - } - s.currentID = s.nextID - return sid +// Add is like Push except it doesn't push the current transformation to the +// stack. +func (t TransformOp) Add(o *Ops) { + t.add(o, false) } -func (s *stack) check(sid stackID) { - if s.currentID != sid.id { - panic("unbalanced operation") +func (t TransformOp) add(o *Ops, push bool) { + data := ops.Write(&o.Internal, ops.TypeTransformLen) + data[0] = byte(ops.TypeTransform) + if push { + data[1] = 1 } -} - -func (s *stack) pop(sid stackID) { - s.check(sid) - s.currentID = sid.prev + bo := binary.LittleEndian + a, b, c, d, e, f := t.t.Elems() + bo.PutUint32(data[2:], math.Float32bits(a)) + bo.PutUint32(data[2+4*1:], math.Float32bits(b)) + bo.PutUint32(data[2+4*2:], math.Float32bits(c)) + bo.PutUint32(data[2+4*3:], math.Float32bits(d)) + bo.PutUint32(data[2+4*4:], math.Float32bits(e)) + bo.PutUint32(data[2+4*5:], math.Float32bits(f)) +} + +func (t TransformStack) Pop() { + ops.PopOp(t.ops, ops.TransStack, t.id, t.macroID) + data := ops.Write(t.ops, ops.TypePopTransformLen) + data[0] = byte(ops.TypePopTransform) } diff --git a/vendor/gioui.org/op/paint/doc.go b/vendor/gioui.org/op/paint/doc.go index b7b569a..bbec006 100644 --- a/vendor/gioui.org/op/paint/doc.go +++ b/vendor/gioui.org/op/paint/doc.go @@ -3,10 +3,13 @@ /* Package paint provides drawing operations for 2D graphics. -The PaintOp operation draws the current brush into a rectangular -area, taking the current clip path and transformation into account. +The PaintOp operation fills the current clip with the current brush, taking the +current transformation into account. Drawing outside the current clip area is +ignored. The current brush is set by either a ColorOp for a constant color, or -ImageOp for an image. +ImageOp for an image, or LinearGradientOp for gradients. + +All color.NRGBA values are in the sRGB color space. */ package paint diff --git a/vendor/gioui.org/op/paint/paint.go b/vendor/gioui.org/op/paint/paint.go index b4a9a56..1c99297 100644 --- a/vendor/gioui.org/op/paint/paint.go +++ b/vendor/gioui.org/op/paint/paint.go @@ -10,20 +10,15 @@ import ( "math" "gioui.org/f32" - "gioui.org/internal/opconst" + "gioui.org/internal/ops" "gioui.org/op" + "gioui.org/op/clip" ) // ImageOp sets the brush to an image. -// -// Note: the ImageOp may keep a reference to the backing image. -// See NewImageOp for details. type ImageOp struct { - // Rect is the section if the backing image to use. - Rect image.Rectangle - uniform bool - color color.RGBA + color color.NRGBA src *image.RGBA // handle is a key to uniquely identify this ImageOp @@ -33,20 +28,23 @@ type ImageOp struct { // ColorOp sets the brush to a constant color. type ColorOp struct { - Color color.RGBA + Color color.NRGBA } -// PaintOp fills an area with the current brush, respecting the -// current clip path and transformation. +// LinearGradientOp sets the brush to a gradient starting at stop1 with color1 and +// ending at stop2 with color2. +type LinearGradientOp struct { + Stop1 f32.Point + Color1 color.NRGBA + Stop2 f32.Point + Color2 color.NRGBA +} + +// PaintOp fills the current clip area with the current brush. type PaintOp struct { - // Rect is the destination area to paint. If necessary, the brush is - // scaled to cover the rectangle area. - Rect f32.Rectangle } -// NewImageOp creates an ImageOp backed by src. See -// gioui.org/io/system.FrameEvent for a description of when data -// referenced by operations is safe to re-use. +// NewImageOp creates an ImageOp backed by src. // // NewImageOp assumes the backing image is immutable, and may cache a // copy of its contents in a GPU-friendly way. Create new ImageOps to @@ -55,19 +53,15 @@ type PaintOp struct { func NewImageOp(src image.Image) ImageOp { switch src := src.(type) { case *image.Uniform: - col := color.RGBAModel.Convert(src.C).(color.RGBA) + col := color.NRGBAModel.Convert(src.C).(color.NRGBA) return ImageOp{ uniform: true, color: col, } case *image.RGBA: - bounds := src.Bounds() - if bounds.Min == (image.Point{}) && src.Stride == bounds.Dx()*4 { - return ImageOp{ - Rect: src.Bounds(), - src: src, - handle: new(int), - } + return ImageOp{ + src: src, + handle: new(int), } } @@ -78,7 +72,6 @@ func NewImageOp(src image.Image) ImageOp { }) draw.Draw(dst, dst.Bounds(), src, src.Bounds().Min, draw.Src) return ImageOp{ - Rect: dst.Bounds(), src: dst, handle: new(int), } @@ -97,31 +90,58 @@ func (i ImageOp) Add(o *op.Ops) { Color: i.color, }.Add(o) return + } else if i.src == nil || i.src.Bounds().Empty() { + return } - data := o.Write(opconst.TypeImageLen, i.src, i.handle) - data[0] = byte(opconst.TypeImage) - bo := binary.LittleEndian - bo.PutUint32(data[1:], uint32(i.Rect.Min.X)) - bo.PutUint32(data[5:], uint32(i.Rect.Min.Y)) - bo.PutUint32(data[9:], uint32(i.Rect.Max.X)) - bo.PutUint32(data[13:], uint32(i.Rect.Max.Y)) + data := ops.Write2(&o.Internal, ops.TypeImageLen, i.src, i.handle) + data[0] = byte(ops.TypeImage) } func (c ColorOp) Add(o *op.Ops) { - data := o.Write(opconst.TypeColorLen) - data[0] = byte(opconst.TypeColor) + data := ops.Write(&o.Internal, ops.TypeColorLen) + data[0] = byte(ops.TypeColor) data[1] = c.Color.R data[2] = c.Color.G data[3] = c.Color.B data[4] = c.Color.A } -func (d PaintOp) Add(o *op.Ops) { - data := o.Write(opconst.TypePaintLen) - data[0] = byte(opconst.TypePaint) +func (c LinearGradientOp) Add(o *op.Ops) { + data := ops.Write(&o.Internal, ops.TypeLinearGradientLen) + data[0] = byte(ops.TypeLinearGradient) + bo := binary.LittleEndian - bo.PutUint32(data[1:], math.Float32bits(d.Rect.Min.X)) - bo.PutUint32(data[5:], math.Float32bits(d.Rect.Min.Y)) - bo.PutUint32(data[9:], math.Float32bits(d.Rect.Max.X)) - bo.PutUint32(data[13:], math.Float32bits(d.Rect.Max.Y)) + bo.PutUint32(data[1:], math.Float32bits(c.Stop1.X)) + bo.PutUint32(data[5:], math.Float32bits(c.Stop1.Y)) + bo.PutUint32(data[9:], math.Float32bits(c.Stop2.X)) + bo.PutUint32(data[13:], math.Float32bits(c.Stop2.Y)) + + data[17+0] = c.Color1.R + data[17+1] = c.Color1.G + data[17+2] = c.Color1.B + data[17+3] = c.Color1.A + data[21+0] = c.Color2.R + data[21+1] = c.Color2.G + data[21+2] = c.Color2.B + data[21+3] = c.Color2.A +} + +func (d PaintOp) Add(o *op.Ops) { + data := ops.Write(&o.Internal, ops.TypePaintLen) + data[0] = byte(ops.TypePaint) +} + +// FillShape fills the clip shape with a color. +func FillShape(ops *op.Ops, c color.NRGBA, shape clip.Op) { + defer shape.Push(ops).Pop() + Fill(ops, c) +} + +// Fill paints an infinitely large plane with the provided color. It +// is intended to be used with a clip.Op already in place to limit +// the painted area. Use FillShape unless you need to paint several +// times within the same clip.Op. +func Fill(ops *op.Ops, c color.NRGBA) { + ColorOp{Color: c}.Add(ops) + PaintOp{}.Add(ops) } diff --git a/vendor/gioui.org/shader/LICENSE b/vendor/gioui.org/shader/LICENSE new file mode 100644 index 0000000..81f4733 --- /dev/null +++ b/vendor/gioui.org/shader/LICENSE @@ -0,0 +1,63 @@ +This project is provided under the terms of the UNLICENSE or +the MIT license denoted by the following SPDX identifier: + +SPDX-License-Identifier: Unlicense OR MIT + +You may use the project under the terms of either license. + +Both licenses are reproduced below. + +---- +The MIT License (MIT) + +Copyright (c) 2019 The Gio authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +--- + + + +--- +The UNLICENSE + +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to <https://unlicense.org/> +--- diff --git a/vendor/gioui.org/shader/README.md b/vendor/gioui.org/shader/README.md new file mode 100644 index 0000000..4ea4867 --- /dev/null +++ b/vendor/gioui.org/shader/README.md @@ -0,0 +1,18 @@ +# GPU programs for the Gio project + +This repository contains the source code for the [Gio](https://gioui.org) +project. It also contains the generators and dereived versions for use with the +GPU APIs supported by Gio. + +# Generating CPU fallbacks + +The `piet/gencpu.sh` script updates the piet-gpu binaries: + +``` +$ cd piet +$ ./gencpu.sh +``` + +## Issues and contributions + +See the [Gio contribution guide](https://gioui.org/doc/contribute). diff --git a/vendor/gioui.org/shader/gio/blit.frag b/vendor/gioui.org/shader/gio/blit.frag new file mode 100644 index 0000000..a88f4dc --- /dev/null +++ b/vendor/gioui.org/shader/gio/blit.frag @@ -0,0 +1,15 @@ +#version 310 es + +// SPDX-License-Identifier: Unlicense OR MIT + +precision mediump float; + +layout(location=0) in highp vec2 vUV; + +{{.Header}} + +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = {{.FetchColorExpr}}; +} diff --git a/vendor/gioui.org/shader/gio/blit.vert b/vendor/gioui.org/shader/gio/blit.vert new file mode 100644 index 0000000..83d041f --- /dev/null +++ b/vendor/gioui.org/shader/gio/blit.vert @@ -0,0 +1,27 @@ +#version 310 es + +// SPDX-License-Identifier: Unlicense OR MIT + +#extension GL_GOOGLE_include_directive : enable + +precision highp float; + +#include "common.h" + +layout(push_constant) uniform Block { + vec4 transform; + vec4 uvTransformR1; + vec4 uvTransformR2; +} _block; + +layout(location = 0) in vec2 pos; + +layout(location = 1) in vec2 uv; + +layout(location = 0) out vec2 vUV; + +void main() { + vec2 p = pos*_block.transform.xy + _block.transform.zw; + gl_Position = vec4(transform3x2(windowTransform, vec3(p, 0)), 1); + vUV = transform3x2(m3x2(_block.uvTransformR1.xyz, _block.uvTransformR2.xyz), vec3(uv,1)).xy; +} diff --git a/vendor/gioui.org/shader/gio/common.h b/vendor/gioui.org/shader/gio/common.h new file mode 100644 index 0000000..9b6dc59 --- /dev/null +++ b/vendor/gioui.org/shader/gio/common.h @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +struct m3x2 { + vec3 r0; + vec3 r1; +}; + +// fboTransform is the transformation that cancels the implied transformation +// between the clip space and the framebuffer. Only two rows are returned. The +// last is implied to be [0, 0, 1]. +const m3x2 fboTransform = m3x2( +#if defined(LANG_HLSL) || defined(LANG_MSL) || defined(LANG_MSLIOS) + vec3(1.0, 0.0, 0.0), + vec3(0.0, -1.0, 0.0) +#else + vec3(1.0, 0.0, 0.0), + vec3(0.0, 1.0, 0.0) +#endif +); + +// windowTransform is the transformation that cancels the implied transformation +// between framebuffer space and window system coordinates. +const m3x2 windowTransform = m3x2( +#if defined(LANG_VULKAN) + vec3(1.0, 0.0, 0.0), + vec3(0.0, 1.0, 0.0) +#else + vec3(1.0, 0.0, 0.0), + vec3(0.0, -1.0, 0.0) +#endif +); + +vec3 transform3x2(m3x2 t, vec3 v) { + return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v)); +} diff --git a/vendor/gioui.org/shader/gio/copy.frag b/vendor/gioui.org/shader/gio/copy.frag new file mode 100644 index 0000000..048d9e7 --- /dev/null +++ b/vendor/gioui.org/shader/gio/copy.frag @@ -0,0 +1,24 @@ +#version 310 es + +// SPDX-License-Identifier: Unlicense OR MIT + +precision mediump float; + +layout(binding = 0) uniform sampler2D tex; + +layout(location = 0) in highp vec2 vUV; + +layout(location = 0) out vec4 fragColor; + +vec3 sRGBtoRGB(vec3 rgb) { + bvec3 cutoff = greaterThanEqual(rgb, vec3(0.04045)); + vec3 below = rgb/vec3(12.92); + vec3 above = pow((rgb + vec3(0.055))/vec3(1.055), vec3(2.4)); + return mix(below, above, cutoff); +} + +void main() { + vec4 texel = texture(tex, vUV); + texel.rgb = sRGBtoRGB(texel.rgb); + fragColor = texel; +} diff --git a/vendor/gioui.org/shader/gio/copy.vert b/vendor/gioui.org/shader/gio/copy.vert new file mode 100644 index 0000000..c079b96 --- /dev/null +++ b/vendor/gioui.org/shader/gio/copy.vert @@ -0,0 +1,26 @@ +#version 310 es + +// SPDX-License-Identifier: Unlicense OR MIT + +#extension GL_GOOGLE_include_directive : enable + +precision highp float; + +#include "common.h" + +layout(push_constant) uniform Block { + vec2 scale; + vec2 pos; + vec2 uvScale; +} _block; + +layout(location = 0) in vec2 pos; +layout(location = 1) in vec2 uv; + +layout(location = 0) out vec2 vUV; + +void main() { + vUV = vec2(uv*_block.uvScale); + vec2 p = vec2(pos*_block.scale + _block.pos); + gl_Position = vec4(transform3x2(windowTransform, vec3(p, 0)), 1); +} diff --git a/vendor/gioui.org/shader/gio/cover.frag b/vendor/gioui.org/shader/gio/cover.frag new file mode 100644 index 0000000..34c5e4c --- /dev/null +++ b/vendor/gioui.org/shader/gio/cover.frag @@ -0,0 +1,20 @@ +#version 310 es + +// SPDX-License-Identifier: Unlicense OR MIT + +precision mediump float; + +{{.Header}} + +layout(location = 0) in highp vec2 vCoverUV; +layout(location = 1) in highp vec2 vUV; + +layout(binding = 1) uniform sampler2D cover; + +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = {{.FetchColorExpr}}; + float c = min(abs(texture(cover, vCoverUV).r), 1.0); + fragColor *= c; +} diff --git a/vendor/gioui.org/shader/gio/cover.vert b/vendor/gioui.org/shader/gio/cover.vert new file mode 100644 index 0000000..02fae50 --- /dev/null +++ b/vendor/gioui.org/shader/gio/cover.vert @@ -0,0 +1,31 @@ +#version 310 es + +// SPDX-License-Identifier: Unlicense OR MIT + +#extension GL_GOOGLE_include_directive : enable + +precision highp float; + +#include "common.h" + +layout(push_constant) uniform Block { + vec4 transform; + vec4 uvCoverTransform; + vec4 uvTransformR1; + vec4 uvTransformR2; +} _block; + +layout(location = 0) in vec2 pos; + +layout(location = 0) out vec2 vCoverUV; + +layout(location = 1) in vec2 uv; +layout(location = 1) out vec2 vUV; + +void main() { + vec2 p = vec2(pos*_block.transform.xy + _block.transform.zw); + gl_Position = vec4(transform3x2(windowTransform, vec3(p, 0)), 1); + vUV = transform3x2(m3x2(_block.uvTransformR1.xyz, _block.uvTransformR2.xyz), vec3(uv,1)).xy; + vec3 uv3 = vec3(uv, 1.0); + vCoverUV = (uv3*vec3(_block.uvCoverTransform.xy, 1.0)+vec3(_block.uvCoverTransform.zw, 0.0)).xy; +} diff --git a/vendor/gioui.org/shader/gio/gen.go b/vendor/gioui.org/shader/gio/gen.go new file mode 100644 index 0000000..f26056a --- /dev/null +++ b/vendor/gioui.org/shader/gio/gen.go @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package gio + +//go:generate go run ../cmd/convertshaders -package gio -dir . diff --git a/vendor/gioui.org/shader/gio/input.vert b/vendor/gioui.org/shader/gio/input.vert new file mode 100644 index 0000000..3d0cd50 --- /dev/null +++ b/vendor/gioui.org/shader/gio/input.vert @@ -0,0 +1,15 @@ +#version 310 es + +// SPDX-License-Identifier: Unlicense OR MIT + +#extension GL_GOOGLE_include_directive : enable + +precision highp float; + +#include "common.h" + +layout(location=0) in vec4 position; + +void main() { + gl_Position = vec4(transform3x2(windowTransform, position.xyz), position.w); +} diff --git a/vendor/gioui.org/shader/gio/intersect.frag b/vendor/gioui.org/shader/gio/intersect.frag new file mode 100644 index 0000000..21a126f --- /dev/null +++ b/vendor/gioui.org/shader/gio/intersect.frag @@ -0,0 +1,15 @@ +#version 310 es + +// SPDX-License-Identifier: Unlicense OR MIT + +precision mediump float; + +layout(location = 0) in highp vec2 vUV; + +layout(binding = 0) uniform sampler2D cover; + +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor.r = abs(texture(cover, vUV).r); +} diff --git a/vendor/gioui.org/shader/gio/intersect.vert b/vendor/gioui.org/shader/gio/intersect.vert new file mode 100644 index 0000000..e7ee2fe --- /dev/null +++ b/vendor/gioui.org/shader/gio/intersect.vert @@ -0,0 +1,26 @@ +#version 310 es + +// SPDX-License-Identifier: Unlicense OR MIT + +#extension GL_GOOGLE_include_directive : enable + +precision highp float; + +#include "common.h" + +layout(location = 0) in vec2 pos; +layout(location = 1) in vec2 uv; + +layout(push_constant) uniform Block { + vec4 uvTransform; + vec4 subUVTransform; +} _block; + +layout(location = 0) out vec2 vUV; + +void main() { + vec3 p = transform3x2(fboTransform, vec3(pos, 1.0)); + gl_Position = vec4(p, 1); + vUV = uv.xy*_block.subUVTransform.xy + _block.subUVTransform.zw; + vUV = vUV*_block.uvTransform.xy + _block.uvTransform.zw; +} diff --git a/vendor/gioui.org/shader/gio/material.frag b/vendor/gioui.org/shader/gio/material.frag new file mode 100644 index 0000000..489461e --- /dev/null +++ b/vendor/gioui.org/shader/gio/material.frag @@ -0,0 +1,32 @@ +#version 310 es + +// SPDX-License-Identifier: Unlicense OR MIT + +precision mediump float; + +layout(binding = 0) uniform sampler2D tex; + +layout(location = 0) in highp vec2 vUV; + +layout(location = 0) out vec4 fragColor; + +layout(push_constant) uniform Color { + // If emulateSRGB is set (!= 0), the input texels are sRGB encoded. We save the + // conversion step below, at the cost of texture filtering in sRGB space. + layout(offset=16) float emulateSRGB; +} _color; + +vec3 RGBtosRGB(vec3 rgb) { + bvec3 cutoff = greaterThanEqual(rgb, vec3(0.0031308)); + vec3 below = vec3(12.92)*rgb; + vec3 above = vec3(1.055)*pow(rgb, vec3(0.41666)) - vec3(0.055); + return mix(below, above, cutoff); +} + +void main() { + vec4 texel = texture(tex, vUV); + if (_color.emulateSRGB == 0.0) { + texel.rgb = RGBtosRGB(texel.rgb); + } + fragColor = texel; +} diff --git a/vendor/gioui.org/shader/gio/material.vert b/vendor/gioui.org/shader/gio/material.vert new file mode 100644 index 0000000..22c41a0 --- /dev/null +++ b/vendor/gioui.org/shader/gio/material.vert @@ -0,0 +1,25 @@ +#version 310 es + +// SPDX-License-Identifier: Unlicense OR MIT + +#extension GL_GOOGLE_include_directive : enable + +precision highp float; + +#include "common.h" + +layout(push_constant) uniform Block { + vec2 scale; + vec2 pos; +} _block; + +layout(location = 0) in vec2 pos; +layout(location = 1) in vec2 uv; + +layout(location = 0) out vec2 vUV; + +void main() { + vUV = uv; + vec2 p = vec2(pos*_block.scale + _block.pos); + gl_Position = vec4(transform3x2(fboTransform, vec3(p, 0)), 1); +} diff --git a/vendor/gioui.org/shader/gio/shaders.go b/vendor/gioui.org/shader/gio/shaders.go new file mode 100644 index 0000000..1166c1c --- /dev/null +++ b/vendor/gioui.org/shader/gio/shaders.go @@ -0,0 +1,796 @@ +// Code generated by build.go. DO NOT EDIT. + +package gio + +import ( + _ "embed" + "runtime" + + "gioui.org/shader" +) + +var ( + Shader_blit_frag = [...]shader.Sources{ + { + Name: "blit.frag", + Inputs: []shader.InputLocation{{Name: "vUV", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}}, + Uniforms: shader.UniformsReflection{ + Locations: []shader.UniformLocation{{Name: "_color.color", Type: 0x0, Size: 4, Offset: 112}}, + Size: 16, + }, + }, + { + Name: "blit.frag", + Inputs: []shader.InputLocation{{Name: "vUV", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}}, + Uniforms: shader.UniformsReflection{ + Locations: []shader.UniformLocation{{Name: "_gradient.color1", Type: 0x0, Size: 4, Offset: 96}, {Name: "_gradient.color2", Type: 0x0, Size: 4, Offset: 112}}, + Size: 32, + }, + }, + { + Name: "blit.frag", + Inputs: []shader.InputLocation{{Name: "vUV", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}}, + Textures: []shader.TextureBinding{{Name: "tex", Binding: 0}}, + }, + } + //go:embed zblit.frag.0.spirv + zblit_frag_0_spirv string + //go:embed zblit.frag.0.glsl100es + zblit_frag_0_glsl100es string + //go:embed zblit.frag.0.glsl150 + zblit_frag_0_glsl150 string + //go:embed zblit.frag.0.dxbc + zblit_frag_0_dxbc string + //go:embed zblit.frag.0.metallibmacos + zblit_frag_0_metallibmacos string + //go:embed zblit.frag.0.metallibios + zblit_frag_0_metallibios string + //go:embed zblit.frag.0.metallibiossimulator + zblit_frag_0_metallibiossimulator string + //go:embed zblit.frag.1.spirv + zblit_frag_1_spirv string + //go:embed zblit.frag.1.glsl100es + zblit_frag_1_glsl100es string + //go:embed zblit.frag.1.glsl150 + zblit_frag_1_glsl150 string + //go:embed zblit.frag.1.dxbc + zblit_frag_1_dxbc string + //go:embed zblit.frag.1.metallibmacos + zblit_frag_1_metallibmacos string + //go:embed zblit.frag.1.metallibios + zblit_frag_1_metallibios string + //go:embed zblit.frag.1.metallibiossimulator + zblit_frag_1_metallibiossimulator string + //go:embed zblit.frag.2.spirv + zblit_frag_2_spirv string + //go:embed zblit.frag.2.glsl100es + zblit_frag_2_glsl100es string + //go:embed zblit.frag.2.glsl150 + zblit_frag_2_glsl150 string + //go:embed zblit.frag.2.dxbc + zblit_frag_2_dxbc string + //go:embed zblit.frag.2.metallibmacos + zblit_frag_2_metallibmacos string + //go:embed zblit.frag.2.metallibios + zblit_frag_2_metallibios string + //go:embed zblit.frag.2.metallibiossimulator + zblit_frag_2_metallibiossimulator string + Shader_blit_vert = shader.Sources{ + Name: "blit.vert", + Inputs: []shader.InputLocation{{Name: "pos", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "uv", Location: 1, Semantic: "TEXCOORD", SemanticIndex: 1, Type: 0x0, Size: 2}}, + Uniforms: shader.UniformsReflection{ + Locations: []shader.UniformLocation{{Name: "_block.transform", Type: 0x0, Size: 4, Offset: 0}, {Name: "_block.uvTransformR1", Type: 0x0, Size: 4, Offset: 16}, {Name: "_block.uvTransformR2", Type: 0x0, Size: 4, Offset: 32}}, + Size: 48, + }, + } + //go:embed zblit.vert.0.spirv + zblit_vert_0_spirv string + //go:embed zblit.vert.0.glsl100es + zblit_vert_0_glsl100es string + //go:embed zblit.vert.0.glsl150 + zblit_vert_0_glsl150 string + //go:embed zblit.vert.0.dxbc + zblit_vert_0_dxbc string + //go:embed zblit.vert.0.metallibmacos + zblit_vert_0_metallibmacos string + //go:embed zblit.vert.0.metallibios + zblit_vert_0_metallibios string + //go:embed zblit.vert.0.metallibiossimulator + zblit_vert_0_metallibiossimulator string + Shader_copy_frag = shader.Sources{ + Name: "copy.frag", + Inputs: []shader.InputLocation{{Name: "vUV", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}}, + Textures: []shader.TextureBinding{{Name: "tex", Binding: 0}}, + } + //go:embed zcopy.frag.0.spirv + zcopy_frag_0_spirv string + //go:embed zcopy.frag.0.glsl100es + zcopy_frag_0_glsl100es string + //go:embed zcopy.frag.0.glsl150 + zcopy_frag_0_glsl150 string + //go:embed zcopy.frag.0.dxbc + zcopy_frag_0_dxbc string + //go:embed zcopy.frag.0.metallibmacos + zcopy_frag_0_metallibmacos string + //go:embed zcopy.frag.0.metallibios + zcopy_frag_0_metallibios string + //go:embed zcopy.frag.0.metallibiossimulator + zcopy_frag_0_metallibiossimulator string + Shader_copy_vert = shader.Sources{ + Name: "copy.vert", + Inputs: []shader.InputLocation{{Name: "pos", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "uv", Location: 1, Semantic: "TEXCOORD", SemanticIndex: 1, Type: 0x0, Size: 2}}, + Uniforms: shader.UniformsReflection{ + Locations: []shader.UniformLocation{{Name: "_block.scale", Type: 0x0, Size: 2, Offset: 0}, {Name: "_block.pos", Type: 0x0, Size: 2, Offset: 8}, {Name: "_block.uvScale", Type: 0x0, Size: 2, Offset: 16}}, + Size: 24, + }, + } + //go:embed zcopy.vert.0.spirv + zcopy_vert_0_spirv string + //go:embed zcopy.vert.0.glsl100es + zcopy_vert_0_glsl100es string + //go:embed zcopy.vert.0.glsl150 + zcopy_vert_0_glsl150 string + //go:embed zcopy.vert.0.dxbc + zcopy_vert_0_dxbc string + //go:embed zcopy.vert.0.metallibmacos + zcopy_vert_0_metallibmacos string + //go:embed zcopy.vert.0.metallibios + zcopy_vert_0_metallibios string + //go:embed zcopy.vert.0.metallibiossimulator + zcopy_vert_0_metallibiossimulator string + Shader_cover_frag = [...]shader.Sources{ + { + Name: "cover.frag", + Inputs: []shader.InputLocation{{Name: "vCoverUV", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "vUV", Location: 1, Semantic: "TEXCOORD", SemanticIndex: 1, Type: 0x0, Size: 2}}, + Uniforms: shader.UniformsReflection{ + Locations: []shader.UniformLocation{{Name: "_color.color", Type: 0x0, Size: 4, Offset: 112}}, + Size: 16, + }, + Textures: []shader.TextureBinding{{Name: "cover", Binding: 1}}, + }, + { + Name: "cover.frag", + Inputs: []shader.InputLocation{{Name: "vCoverUV", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "vUV", Location: 1, Semantic: "TEXCOORD", SemanticIndex: 1, Type: 0x0, Size: 2}}, + Uniforms: shader.UniformsReflection{ + Locations: []shader.UniformLocation{{Name: "_gradient.color1", Type: 0x0, Size: 4, Offset: 96}, {Name: "_gradient.color2", Type: 0x0, Size: 4, Offset: 112}}, + Size: 32, + }, + Textures: []shader.TextureBinding{{Name: "cover", Binding: 1}}, + }, + { + Name: "cover.frag", + Inputs: []shader.InputLocation{{Name: "vCoverUV", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "vUV", Location: 1, Semantic: "TEXCOORD", SemanticIndex: 1, Type: 0x0, Size: 2}}, + Textures: []shader.TextureBinding{{Name: "tex", Binding: 0}, {Name: "cover", Binding: 1}}, + }, + } + //go:embed zcover.frag.0.spirv + zcover_frag_0_spirv string + //go:embed zcover.frag.0.glsl100es + zcover_frag_0_glsl100es string + //go:embed zcover.frag.0.glsl150 + zcover_frag_0_glsl150 string + //go:embed zcover.frag.0.dxbc + zcover_frag_0_dxbc string + //go:embed zcover.frag.0.metallibmacos + zcover_frag_0_metallibmacos string + //go:embed zcover.frag.0.metallibios + zcover_frag_0_metallibios string + //go:embed zcover.frag.0.metallibiossimulator + zcover_frag_0_metallibiossimulator string + //go:embed zcover.frag.1.spirv + zcover_frag_1_spirv string + //go:embed zcover.frag.1.glsl100es + zcover_frag_1_glsl100es string + //go:embed zcover.frag.1.glsl150 + zcover_frag_1_glsl150 string + //go:embed zcover.frag.1.dxbc + zcover_frag_1_dxbc string + //go:embed zcover.frag.1.metallibmacos + zcover_frag_1_metallibmacos string + //go:embed zcover.frag.1.metallibios + zcover_frag_1_metallibios string + //go:embed zcover.frag.1.metallibiossimulator + zcover_frag_1_metallibiossimulator string + //go:embed zcover.frag.2.spirv + zcover_frag_2_spirv string + //go:embed zcover.frag.2.glsl100es + zcover_frag_2_glsl100es string + //go:embed zcover.frag.2.glsl150 + zcover_frag_2_glsl150 string + //go:embed zcover.frag.2.dxbc + zcover_frag_2_dxbc string + //go:embed zcover.frag.2.metallibmacos + zcover_frag_2_metallibmacos string + //go:embed zcover.frag.2.metallibios + zcover_frag_2_metallibios string + //go:embed zcover.frag.2.metallibiossimulator + zcover_frag_2_metallibiossimulator string + Shader_cover_vert = shader.Sources{ + Name: "cover.vert", + Inputs: []shader.InputLocation{{Name: "pos", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "uv", Location: 1, Semantic: "TEXCOORD", SemanticIndex: 1, Type: 0x0, Size: 2}}, + Uniforms: shader.UniformsReflection{ + Locations: []shader.UniformLocation{{Name: "_block.transform", Type: 0x0, Size: 4, Offset: 0}, {Name: "_block.uvCoverTransform", Type: 0x0, Size: 4, Offset: 16}, {Name: "_block.uvTransformR1", Type: 0x0, Size: 4, Offset: 32}, {Name: "_block.uvTransformR2", Type: 0x0, Size: 4, Offset: 48}}, + Size: 64, + }, + } + //go:embed zcover.vert.0.spirv + zcover_vert_0_spirv string + //go:embed zcover.vert.0.glsl100es + zcover_vert_0_glsl100es string + //go:embed zcover.vert.0.glsl150 + zcover_vert_0_glsl150 string + //go:embed zcover.vert.0.dxbc + zcover_vert_0_dxbc string + //go:embed zcover.vert.0.metallibmacos + zcover_vert_0_metallibmacos string + //go:embed zcover.vert.0.metallibios + zcover_vert_0_metallibios string + //go:embed zcover.vert.0.metallibiossimulator + zcover_vert_0_metallibiossimulator string + Shader_input_vert = shader.Sources{ + Name: "input.vert", + Inputs: []shader.InputLocation{{Name: "position", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 4}}, + } + //go:embed zinput.vert.0.spirv + zinput_vert_0_spirv string + //go:embed zinput.vert.0.glsl100es + zinput_vert_0_glsl100es string + //go:embed zinput.vert.0.glsl150 + zinput_vert_0_glsl150 string + //go:embed zinput.vert.0.dxbc + zinput_vert_0_dxbc string + //go:embed zinput.vert.0.metallibmacos + zinput_vert_0_metallibmacos string + //go:embed zinput.vert.0.metallibios + zinput_vert_0_metallibios string + //go:embed zinput.vert.0.metallibiossimulator + zinput_vert_0_metallibiossimulator string + Shader_intersect_frag = shader.Sources{ + Name: "intersect.frag", + Inputs: []shader.InputLocation{{Name: "vUV", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}}, + Textures: []shader.TextureBinding{{Name: "cover", Binding: 0}}, + } + //go:embed zintersect.frag.0.spirv + zintersect_frag_0_spirv string + //go:embed zintersect.frag.0.glsl100es + zintersect_frag_0_glsl100es string + //go:embed zintersect.frag.0.glsl150 + zintersect_frag_0_glsl150 string + //go:embed zintersect.frag.0.dxbc + zintersect_frag_0_dxbc string + //go:embed zintersect.frag.0.metallibmacos + zintersect_frag_0_metallibmacos string + //go:embed zintersect.frag.0.metallibios + zintersect_frag_0_metallibios string + //go:embed zintersect.frag.0.metallibiossimulator + zintersect_frag_0_metallibiossimulator string + Shader_intersect_vert = shader.Sources{ + Name: "intersect.vert", + Inputs: []shader.InputLocation{{Name: "pos", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "uv", Location: 1, Semantic: "TEXCOORD", SemanticIndex: 1, Type: 0x0, Size: 2}}, + Uniforms: shader.UniformsReflection{ + Locations: []shader.UniformLocation{{Name: "_block.uvTransform", Type: 0x0, Size: 4, Offset: 0}, {Name: "_block.subUVTransform", Type: 0x0, Size: 4, Offset: 16}}, + Size: 32, + }, + } + //go:embed zintersect.vert.0.spirv + zintersect_vert_0_spirv string + //go:embed zintersect.vert.0.glsl100es + zintersect_vert_0_glsl100es string + //go:embed zintersect.vert.0.glsl150 + zintersect_vert_0_glsl150 string + //go:embed zintersect.vert.0.dxbc + zintersect_vert_0_dxbc string + //go:embed zintersect.vert.0.metallibmacos + zintersect_vert_0_metallibmacos string + //go:embed zintersect.vert.0.metallibios + zintersect_vert_0_metallibios string + //go:embed zintersect.vert.0.metallibiossimulator + zintersect_vert_0_metallibiossimulator string + Shader_material_frag = shader.Sources{ + Name: "material.frag", + Inputs: []shader.InputLocation{{Name: "vUV", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}}, + Uniforms: shader.UniformsReflection{ + Locations: []shader.UniformLocation{{Name: "_color.emulateSRGB", Type: 0x0, Size: 1, Offset: 16}}, + Size: 4, + }, + Textures: []shader.TextureBinding{{Name: "tex", Binding: 0}}, + } + //go:embed zmaterial.frag.0.spirv + zmaterial_frag_0_spirv string + //go:embed zmaterial.frag.0.glsl100es + zmaterial_frag_0_glsl100es string + //go:embed zmaterial.frag.0.glsl150 + zmaterial_frag_0_glsl150 string + //go:embed zmaterial.frag.0.dxbc + zmaterial_frag_0_dxbc string + //go:embed zmaterial.frag.0.metallibmacos + zmaterial_frag_0_metallibmacos string + //go:embed zmaterial.frag.0.metallibios + zmaterial_frag_0_metallibios string + //go:embed zmaterial.frag.0.metallibiossimulator + zmaterial_frag_0_metallibiossimulator string + Shader_material_vert = shader.Sources{ + Name: "material.vert", + Inputs: []shader.InputLocation{{Name: "pos", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "uv", Location: 1, Semantic: "TEXCOORD", SemanticIndex: 1, Type: 0x0, Size: 2}}, + Uniforms: shader.UniformsReflection{ + Locations: []shader.UniformLocation{{Name: "_block.scale", Type: 0x0, Size: 2, Offset: 0}, {Name: "_block.pos", Type: 0x0, Size: 2, Offset: 8}}, + Size: 16, + }, + } + //go:embed zmaterial.vert.0.spirv + zmaterial_vert_0_spirv string + //go:embed zmaterial.vert.0.glsl100es + zmaterial_vert_0_glsl100es string + //go:embed zmaterial.vert.0.glsl150 + zmaterial_vert_0_glsl150 string + //go:embed zmaterial.vert.0.dxbc + zmaterial_vert_0_dxbc string + //go:embed zmaterial.vert.0.metallibmacos + zmaterial_vert_0_metallibmacos string + //go:embed zmaterial.vert.0.metallibios + zmaterial_vert_0_metallibios string + //go:embed zmaterial.vert.0.metallibiossimulator + zmaterial_vert_0_metallibiossimulator string + Shader_simple_frag = shader.Sources{ + Name: "simple.frag", + } + //go:embed zsimple.frag.0.spirv + zsimple_frag_0_spirv string + //go:embed zsimple.frag.0.glsl100es + zsimple_frag_0_glsl100es string + //go:embed zsimple.frag.0.glsl150 + zsimple_frag_0_glsl150 string + //go:embed zsimple.frag.0.dxbc + zsimple_frag_0_dxbc string + //go:embed zsimple.frag.0.metallibmacos + zsimple_frag_0_metallibmacos string + //go:embed zsimple.frag.0.metallibios + zsimple_frag_0_metallibios string + //go:embed zsimple.frag.0.metallibiossimulator + zsimple_frag_0_metallibiossimulator string + Shader_stencil_frag = shader.Sources{ + Name: "stencil.frag", + Inputs: []shader.InputLocation{{Name: "vFrom", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 2}, {Name: "vCtrl", Location: 1, Semantic: "TEXCOORD", SemanticIndex: 1, Type: 0x0, Size: 2}, {Name: "vTo", Location: 2, Semantic: "TEXCOORD", SemanticIndex: 2, Type: 0x0, Size: 2}}, + } + //go:embed zstencil.frag.0.spirv + zstencil_frag_0_spirv string + //go:embed zstencil.frag.0.glsl100es + zstencil_frag_0_glsl100es string + //go:embed zstencil.frag.0.glsl150 + zstencil_frag_0_glsl150 string + //go:embed zstencil.frag.0.dxbc + zstencil_frag_0_dxbc string + //go:embed zstencil.frag.0.metallibmacos + zstencil_frag_0_metallibmacos string + //go:embed zstencil.frag.0.metallibios + zstencil_frag_0_metallibios string + //go:embed zstencil.frag.0.metallibiossimulator + zstencil_frag_0_metallibiossimulator string + Shader_stencil_vert = shader.Sources{ + Name: "stencil.vert", + Inputs: []shader.InputLocation{{Name: "corner", Location: 0, Semantic: "TEXCOORD", SemanticIndex: 0, Type: 0x0, Size: 1}, {Name: "maxy", Location: 1, Semantic: "TEXCOORD", SemanticIndex: 1, Type: 0x0, Size: 1}, {Name: "from", Location: 2, Semantic: "TEXCOORD", SemanticIndex: 2, Type: 0x0, Size: 2}, {Name: "ctrl", Location: 3, Semantic: "TEXCOORD", SemanticIndex: 3, Type: 0x0, Size: 2}, {Name: "to", Location: 4, Semantic: "TEXCOORD", SemanticIndex: 4, Type: 0x0, Size: 2}}, + Uniforms: shader.UniformsReflection{ + Locations: []shader.UniformLocation{{Name: "_block.transform", Type: 0x0, Size: 4, Offset: 0}, {Name: "_block.pathOffset", Type: 0x0, Size: 2, Offset: 16}}, + Size: 24, + }, + } + //go:embed zstencil.vert.0.spirv + zstencil_vert_0_spirv string + //go:embed zstencil.vert.0.glsl100es + zstencil_vert_0_glsl100es string + //go:embed zstencil.vert.0.glsl150 + zstencil_vert_0_glsl150 string + //go:embed zstencil.vert.0.dxbc + zstencil_vert_0_dxbc string + //go:embed zstencil.vert.0.metallibmacos + zstencil_vert_0_metallibmacos string + //go:embed zstencil.vert.0.metallibios + zstencil_vert_0_metallibios string + //go:embed zstencil.vert.0.metallibiossimulator + zstencil_vert_0_metallibiossimulator string +) + +func init() { + const ( + opengles = runtime.GOOS == "linux" || runtime.GOOS == "freebsd" || runtime.GOOS == "openbsd" || runtime.GOOS == "windows" || runtime.GOOS == "js" || runtime.GOOS == "android" || runtime.GOOS == "darwin" || runtime.GOOS == "ios" + opengl = runtime.GOOS == "darwin" + d3d11 = runtime.GOOS == "windows" + vulkan = runtime.GOOS == "linux" || runtime.GOOS == "android" + ) + if vulkan { + Shader_blit_frag[0].SPIRV = zblit_frag_0_spirv + } + if opengles { + Shader_blit_frag[0].GLSL100ES = zblit_frag_0_glsl100es + } + if opengl { + Shader_blit_frag[0].GLSL150 = zblit_frag_0_glsl150 + } + if d3d11 { + Shader_blit_frag[0].DXBC = zblit_frag_0_dxbc + } + if runtime.GOOS == "darwin" { + Shader_blit_frag[0].MetalLib = zblit_frag_0_metallibmacos + } + if runtime.GOOS == "ios" { + if runtime.GOARCH == "amd64" { + Shader_blit_frag[0].MetalLib = zblit_frag_0_metallibiossimulator + } else { + Shader_blit_frag[0].MetalLib = zblit_frag_0_metallibios + } + } + if vulkan { + Shader_blit_frag[1].SPIRV = zblit_frag_1_spirv + } + if opengles { + Shader_blit_frag[1].GLSL100ES = zblit_frag_1_glsl100es + } + if opengl { + Shader_blit_frag[1].GLSL150 = zblit_frag_1_glsl150 + } + if d3d11 { + Shader_blit_frag[1].DXBC = zblit_frag_1_dxbc + } + if runtime.GOOS == "darwin" { + Shader_blit_frag[1].MetalLib = zblit_frag_1_metallibmacos + } + if runtime.GOOS == "ios" { + if runtime.GOARCH == "amd64" { + Shader_blit_frag[1].MetalLib = zblit_frag_1_metallibiossimulator + } else { + Shader_blit_frag[1].MetalLib = zblit_frag_1_metallibios + } + } + if vulkan { + Shader_blit_frag[2].SPIRV = zblit_frag_2_spirv + } + if opengles { + Shader_blit_frag[2].GLSL100ES = zblit_frag_2_glsl100es + } + if opengl { + Shader_blit_frag[2].GLSL150 = zblit_frag_2_glsl150 + } + if d3d11 { + Shader_blit_frag[2].DXBC = zblit_frag_2_dxbc + } + if runtime.GOOS == "darwin" { + Shader_blit_frag[2].MetalLib = zblit_frag_2_metallibmacos + } + if runtime.GOOS == "ios" { + if runtime.GOARCH == "amd64" { + Shader_blit_frag[2].MetalLib = zblit_frag_2_metallibiossimulator + } else { + Shader_blit_frag[2].MetalLib = zblit_frag_2_metallibios + } + } + if vulkan { + Shader_blit_vert.SPIRV = zblit_vert_0_spirv + } + if opengles { + Shader_blit_vert.GLSL100ES = zblit_vert_0_glsl100es + } + if opengl { + Shader_blit_vert.GLSL150 = zblit_vert_0_glsl150 + } + if d3d11 { + Shader_blit_vert.DXBC = zblit_vert_0_dxbc + } + if runtime.GOOS == "darwin" { + Shader_blit_vert.MetalLib = zblit_vert_0_metallibmacos + } + if runtime.GOOS == "ios" { + if runtime.GOARCH == "amd64" { + Shader_blit_vert.MetalLib = zblit_vert_0_metallibiossimulator + } else { + Shader_blit_vert.MetalLib = zblit_vert_0_metallibios + } + } + if vulkan { + Shader_copy_frag.SPIRV = zcopy_frag_0_spirv + } + if opengles { + Shader_copy_frag.GLSL100ES = zcopy_frag_0_glsl100es + } + if opengl { + Shader_copy_frag.GLSL150 = zcopy_frag_0_glsl150 + } + if d3d11 { + Shader_copy_frag.DXBC = zcopy_frag_0_dxbc + } + if runtime.GOOS == "darwin" { + Shader_copy_frag.MetalLib = zcopy_frag_0_metallibmacos + } + if runtime.GOOS == "ios" { + if runtime.GOARCH == "amd64" { + Shader_copy_frag.MetalLib = zcopy_frag_0_metallibiossimulator + } else { + Shader_copy_frag.MetalLib = zcopy_frag_0_metallibios + } + } + if vulkan { + Shader_copy_vert.SPIRV = zcopy_vert_0_spirv + } + if opengles { + Shader_copy_vert.GLSL100ES = zcopy_vert_0_glsl100es + } + if opengl { + Shader_copy_vert.GLSL150 = zcopy_vert_0_glsl150 + } + if d3d11 { + Shader_copy_vert.DXBC = zcopy_vert_0_dxbc + } + if runtime.GOOS == "darwin" { + Shader_copy_vert.MetalLib = zcopy_vert_0_metallibmacos + } + if runtime.GOOS == "ios" { + if runtime.GOARCH == "amd64" { + Shader_copy_vert.MetalLib = zcopy_vert_0_metallibiossimulator + } else { + Shader_copy_vert.MetalLib = zcopy_vert_0_metallibios + } + } + if vulkan { + Shader_cover_frag[0].SPIRV = zcover_frag_0_spirv + } + if opengles { + Shader_cover_frag[0].GLSL100ES = zcover_frag_0_glsl100es + } + if opengl { + Shader_cover_frag[0].GLSL150 = zcover_frag_0_glsl150 + } + if d3d11 { + Shader_cover_frag[0].DXBC = zcover_frag_0_dxbc + } + if runtime.GOOS == "darwin" { + Shader_cover_frag[0].MetalLib = zcover_frag_0_metallibmacos + } + if runtime.GOOS == "ios" { + if runtime.GOARCH == "amd64" { + Shader_cover_frag[0].MetalLib = zcover_frag_0_metallibiossimulator + } else { + Shader_cover_frag[0].MetalLib = zcover_frag_0_metallibios + } + } + if vulkan { + Shader_cover_frag[1].SPIRV = zcover_frag_1_spirv + } + if opengles { + Shader_cover_frag[1].GLSL100ES = zcover_frag_1_glsl100es + } + if opengl { + Shader_cover_frag[1].GLSL150 = zcover_frag_1_glsl150 + } + if d3d11 { + Shader_cover_frag[1].DXBC = zcover_frag_1_dxbc + } + if runtime.GOOS == "darwin" { + Shader_cover_frag[1].MetalLib = zcover_frag_1_metallibmacos + } + if runtime.GOOS == "ios" { + if runtime.GOARCH == "amd64" { + Shader_cover_frag[1].MetalLib = zcover_frag_1_metallibiossimulator + } else { + Shader_cover_frag[1].MetalLib = zcover_frag_1_metallibios + } + } + if vulkan { + Shader_cover_frag[2].SPIRV = zcover_frag_2_spirv + } + if opengles { + Shader_cover_frag[2].GLSL100ES = zcover_frag_2_glsl100es + } + if opengl { + Shader_cover_frag[2].GLSL150 = zcover_frag_2_glsl150 + } + if d3d11 { + Shader_cover_frag[2].DXBC = zcover_frag_2_dxbc + } + if runtime.GOOS == "darwin" { + Shader_cover_frag[2].MetalLib = zcover_frag_2_metallibmacos + } + if runtime.GOOS == "ios" { + if runtime.GOARCH == "amd64" { + Shader_cover_frag[2].MetalLib = zcover_frag_2_metallibiossimulator + } else { + Shader_cover_frag[2].MetalLib = zcover_frag_2_metallibios + } + } + if vulkan { + Shader_cover_vert.SPIRV = zcover_vert_0_spirv + } + if opengles { + Shader_cover_vert.GLSL100ES = zcover_vert_0_glsl100es + } + if opengl { + Shader_cover_vert.GLSL150 = zcover_vert_0_glsl150 + } + if d3d11 { + Shader_cover_vert.DXBC = zcover_vert_0_dxbc + } + if runtime.GOOS == "darwin" { + Shader_cover_vert.MetalLib = zcover_vert_0_metallibmacos + } + if runtime.GOOS == "ios" { + if runtime.GOARCH == "amd64" { + Shader_cover_vert.MetalLib = zcover_vert_0_metallibiossimulator + } else { + Shader_cover_vert.MetalLib = zcover_vert_0_metallibios + } + } + if vulkan { + Shader_input_vert.SPIRV = zinput_vert_0_spirv + } + if opengles { + Shader_input_vert.GLSL100ES = zinput_vert_0_glsl100es + } + if opengl { + Shader_input_vert.GLSL150 = zinput_vert_0_glsl150 + } + if d3d11 { + Shader_input_vert.DXBC = zinput_vert_0_dxbc + } + if runtime.GOOS == "darwin" { + Shader_input_vert.MetalLib = zinput_vert_0_metallibmacos + } + if runtime.GOOS == "ios" { + if runtime.GOARCH == "amd64" { + Shader_input_vert.MetalLib = zinput_vert_0_metallibiossimulator + } else { + Shader_input_vert.MetalLib = zinput_vert_0_metallibios + } + } + if vulkan { + Shader_intersect_frag.SPIRV = zintersect_frag_0_spirv + } + if opengles { + Shader_intersect_frag.GLSL100ES = zintersect_frag_0_glsl100es + } + if opengl { + Shader_intersect_frag.GLSL150 = zintersect_frag_0_glsl150 + } + if d3d11 { + Shader_intersect_frag.DXBC = zintersect_frag_0_dxbc + } + if runtime.GOOS == "darwin" { + Shader_intersect_frag.MetalLib = zintersect_frag_0_metallibmacos + } + if runtime.GOOS == "ios" { + if runtime.GOARCH == "amd64" { + Shader_intersect_frag.MetalLib = zintersect_frag_0_metallibiossimulator + } else { + Shader_intersect_frag.MetalLib = zintersect_frag_0_metallibios + } + } + if vulkan { + Shader_intersect_vert.SPIRV = zintersect_vert_0_spirv + } + if opengles { + Shader_intersect_vert.GLSL100ES = zintersect_vert_0_glsl100es + } + if opengl { + Shader_intersect_vert.GLSL150 = zintersect_vert_0_glsl150 + } + if d3d11 { + Shader_intersect_vert.DXBC = zintersect_vert_0_dxbc + } + if runtime.GOOS == "darwin" { + Shader_intersect_vert.MetalLib = zintersect_vert_0_metallibmacos + } + if runtime.GOOS == "ios" { + if runtime.GOARCH == "amd64" { + Shader_intersect_vert.MetalLib = zintersect_vert_0_metallibiossimulator + } else { + Shader_intersect_vert.MetalLib = zintersect_vert_0_metallibios + } + } + if vulkan { + Shader_material_frag.SPIRV = zmaterial_frag_0_spirv + } + if opengles { + Shader_material_frag.GLSL100ES = zmaterial_frag_0_glsl100es + } + if opengl { + Shader_material_frag.GLSL150 = zmaterial_frag_0_glsl150 + } + if d3d11 { + Shader_material_frag.DXBC = zmaterial_frag_0_dxbc + } + if runtime.GOOS == "darwin" { + Shader_material_frag.MetalLib = zmaterial_frag_0_metallibmacos + } + if runtime.GOOS == "ios" { + if runtime.GOARCH == "amd64" { + Shader_material_frag.MetalLib = zmaterial_frag_0_metallibiossimulator + } else { + Shader_material_frag.MetalLib = zmaterial_frag_0_metallibios + } + } + if vulkan { + Shader_material_vert.SPIRV = zmaterial_vert_0_spirv + } + if opengles { + Shader_material_vert.GLSL100ES = zmaterial_vert_0_glsl100es + } + if opengl { + Shader_material_vert.GLSL150 = zmaterial_vert_0_glsl150 + } + if d3d11 { + Shader_material_vert.DXBC = zmaterial_vert_0_dxbc + } + if runtime.GOOS == "darwin" { + Shader_material_vert.MetalLib = zmaterial_vert_0_metallibmacos + } + if runtime.GOOS == "ios" { + if runtime.GOARCH == "amd64" { + Shader_material_vert.MetalLib = zmaterial_vert_0_metallibiossimulator + } else { + Shader_material_vert.MetalLib = zmaterial_vert_0_metallibios + } + } + if vulkan { + Shader_simple_frag.SPIRV = zsimple_frag_0_spirv + } + if opengles { + Shader_simple_frag.GLSL100ES = zsimple_frag_0_glsl100es + } + if opengl { + Shader_simple_frag.GLSL150 = zsimple_frag_0_glsl150 + } + if d3d11 { + Shader_simple_frag.DXBC = zsimple_frag_0_dxbc + } + if runtime.GOOS == "darwin" { + Shader_simple_frag.MetalLib = zsimple_frag_0_metallibmacos + } + if runtime.GOOS == "ios" { + if runtime.GOARCH == "amd64" { + Shader_simple_frag.MetalLib = zsimple_frag_0_metallibiossimulator + } else { + Shader_simple_frag.MetalLib = zsimple_frag_0_metallibios + } + } + if vulkan { + Shader_stencil_frag.SPIRV = zstencil_frag_0_spirv + } + if opengles { + Shader_stencil_frag.GLSL100ES = zstencil_frag_0_glsl100es + } + if opengl { + Shader_stencil_frag.GLSL150 = zstencil_frag_0_glsl150 + } + if d3d11 { + Shader_stencil_frag.DXBC = zstencil_frag_0_dxbc + } + if runtime.GOOS == "darwin" { + Shader_stencil_frag.MetalLib = zstencil_frag_0_metallibmacos + } + if runtime.GOOS == "ios" { + if runtime.GOARCH == "amd64" { + Shader_stencil_frag.MetalLib = zstencil_frag_0_metallibiossimulator + } else { + Shader_stencil_frag.MetalLib = zstencil_frag_0_metallibios + } + } + if vulkan { + Shader_stencil_vert.SPIRV = zstencil_vert_0_spirv + } + if opengles { + Shader_stencil_vert.GLSL100ES = zstencil_vert_0_glsl100es + } + if opengl { + Shader_stencil_vert.GLSL150 = zstencil_vert_0_glsl150 + } + if d3d11 { + Shader_stencil_vert.DXBC = zstencil_vert_0_dxbc + } + if runtime.GOOS == "darwin" { + Shader_stencil_vert.MetalLib = zstencil_vert_0_metallibmacos + } + if runtime.GOOS == "ios" { + if runtime.GOARCH == "amd64" { + Shader_stencil_vert.MetalLib = zstencil_vert_0_metallibiossimulator + } else { + Shader_stencil_vert.MetalLib = zstencil_vert_0_metallibios + } + } +} diff --git a/vendor/gioui.org/shader/gio/simple.frag b/vendor/gioui.org/shader/gio/simple.frag new file mode 100644 index 0000000..4614f33 --- /dev/null +++ b/vendor/gioui.org/shader/gio/simple.frag @@ -0,0 +1,11 @@ +#version 310 es + +// SPDX-License-Identifier: Unlicense OR MIT + +precision mediump float; + +layout(location = 0) out vec4 fragColor; + +void main() { + fragColor = vec4(.25, .55, .75, 1.0); +} diff --git a/vendor/gioui.org/shader/gio/stencil.frag b/vendor/gioui.org/shader/gio/stencil.frag new file mode 100644 index 0000000..956dae8 --- /dev/null +++ b/vendor/gioui.org/shader/gio/stencil.frag @@ -0,0 +1,81 @@ +#version 310 es + +// SPDX-License-Identifier: Unlicense OR MIT + +precision mediump float; + +layout(location=0) in highp vec2 vFrom; +layout(location=1) in highp vec2 vCtrl; +layout(location=2) in highp vec2 vTo; + +layout(location = 0) out vec4 fragCover; + +void main() { + float dx = vTo.x - vFrom.x; + // Sort from and to in increasing order so the root below + // is always the positive square root, if any. + // We need the direction of the curve below, so this can't be + // done from the vertex shader. + bool increasing = vTo.x >= vFrom.x; + vec2 left = increasing ? vFrom : vTo; + vec2 right = increasing ? vTo : vFrom; + + // The signed horizontal extent of the fragment. + vec2 extent = clamp(vec2(vFrom.x, vTo.x), -0.5, 0.5); + // Find the t where the curve crosses the middle of the + // extent, x₀. + // Given the Bézier curve with x coordinates P₀, P₁, P₂ + // where P₀ is at the origin, its x coordinate in t + // is given by: + // + // x(t) = 2(1-t)tP₁ + t²P₂ + // + // Rearranging: + // + // x(t) = (P₂ - 2P₁)t² + 2P₁t + // + // Setting x(t) = x₀ and using Muller's quadratic formula ("Citardauq") + // for robustnesss, + // + // t = 2x₀/(2P₁±√(4P₁²+4(P₂-2P₁)x₀)) + // + // which simplifies to + // + // t = x₀/(P₁±√(P₁²+(P₂-2P₁)x₀)) + // + // Setting v = P₂-P₁, + // + // t = x₀/(P₁±√(P₁²+(v-P₁)x₀)) + // + // t lie in [0; 1]; P₂ ≥ P₁ and P₁ ≥ 0 since we split curves where + // the control point lies before the start point or after the end point. + // It can then be shown that only the positive square root is valid. + float midx = mix(extent.x, extent.y, 0.5); + float x0 = midx - left.x; + vec2 p1 = vCtrl - left; + vec2 v = right - vCtrl; + float t = x0/(p1.x+sqrt(p1.x*p1.x+(v.x-p1.x)*x0)); + // Find y(t) on the curve. + float y = mix(mix(left.y, vCtrl.y, t), mix(vCtrl.y, right.y, t), t); + // And the slope. + vec2 d_half = mix(p1, v, t); + float dy = d_half.y/d_half.x; + // Together, y and dy form a line approximation. + + // Compute the fragment area above the line. + // The area is symmetric around dy = 0. Scale slope with extent width. + float width = extent.y - extent.x; + dy = abs(dy*width); + + vec4 sides = vec4(dy*+0.5 + y, dy*-0.5 + y, (+0.5-y)/dy, (-0.5-y)/dy); + sides = clamp(sides+0.5, 0.0, 1.0); + + float area = 0.5*(sides.z - sides.z*sides.y + 1.0 - sides.x+sides.x*sides.w); + area *= width; + + // Work around issue #13. + if (width == 0.0) + area = 0.0; + + fragCover.r = area; +} diff --git a/vendor/gioui.org/shader/gio/stencil.vert b/vendor/gioui.org/shader/gio/stencil.vert new file mode 100644 index 0000000..fd6f28b --- /dev/null +++ b/vendor/gioui.org/shader/gio/stencil.vert @@ -0,0 +1,57 @@ +#version 310 es + +// SPDX-License-Identifier: Unlicense OR MIT + +#extension GL_GOOGLE_include_directive : enable + +precision highp float; + +#include "common.h" + +layout(push_constant) uniform Block { + vec4 transform; + vec2 pathOffset; +} _block; + +layout(location=0) in float corner; +layout(location=1) in float maxy; +layout(location=2) in vec2 from; +layout(location=3) in vec2 ctrl; +layout(location=4) in vec2 to; + +layout(location=0) out vec2 vFrom; +layout(location=1) out vec2 vCtrl; +layout(location=2) out vec2 vTo; + +void main() { + // Add a one pixel overlap so curve quads cover their + // entire curves. Could use conservative rasterization + // if available. + vec2 from = from + _block.pathOffset; + vec2 ctrl = ctrl + _block.pathOffset; + vec2 to = to + _block.pathOffset; + float maxy = maxy + _block.pathOffset.y; + vec2 pos; + float c = corner; + if (c >= 0.375) { + // North. + c -= 0.5; + pos.y = maxy + 1.0; + } else { + // South. + pos.y = min(min(from.y, ctrl.y), to.y) - 1.0; + } + if (c >= 0.125) { + // East. + pos.x = max(max(from.x, ctrl.x), to.x)+1.0; + } else { + // West. + pos.x = min(min(from.x, ctrl.x), to.x)-1.0; + } + vFrom = from-pos; + vCtrl = ctrl-pos; + vTo = to-pos; + pos = pos*_block.transform.xy + _block.transform.zw; + gl_Position = vec4(transform3x2(fboTransform, vec3(pos, 0)), 1); +} + diff --git a/vendor/gioui.org/shader/gio/zblit.frag.0.dxbc b/vendor/gioui.org/shader/gio/zblit.frag.0.dxbc Binary files differnew file mode 100644 index 0000000..45e8355 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.frag.0.dxbc diff --git a/vendor/gioui.org/shader/gio/zblit.frag.0.glsl100es b/vendor/gioui.org/shader/gio/zblit.frag.0.glsl100es new file mode 100644 index 0000000..d7ca41a --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.frag.0.glsl100es @@ -0,0 +1,18 @@ +#version 100 +precision mediump float; +precision highp int; + +struct Color +{ + vec4 color; +}; + +uniform Color _color; + +varying highp vec2 vUV; + +void main() +{ + gl_FragData[0] = _color.color; +} + diff --git a/vendor/gioui.org/shader/gio/zblit.frag.0.glsl150 b/vendor/gioui.org/shader/gio/zblit.frag.0.glsl150 new file mode 100644 index 0000000..9559c85 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.frag.0.glsl150 @@ -0,0 +1,17 @@ +#version 150 + +struct Color +{ + vec4 color; +}; + +uniform Color _color; + +out vec4 fragColor; +in vec2 vUV; + +void main() +{ + fragColor = _color.color; +} + diff --git a/vendor/gioui.org/shader/gio/zblit.frag.0.metallibios b/vendor/gioui.org/shader/gio/zblit.frag.0.metallibios Binary files differnew file mode 100644 index 0000000..63e606f --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.frag.0.metallibios diff --git a/vendor/gioui.org/shader/gio/zblit.frag.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zblit.frag.0.metallibiossimulator Binary files differnew file mode 100644 index 0000000..d8d2589 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.frag.0.metallibiossimulator diff --git a/vendor/gioui.org/shader/gio/zblit.frag.0.metallibmacos b/vendor/gioui.org/shader/gio/zblit.frag.0.metallibmacos Binary files differnew file mode 100644 index 0000000..85946aa --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.frag.0.metallibmacos diff --git a/vendor/gioui.org/shader/gio/zblit.frag.0.spirv b/vendor/gioui.org/shader/gio/zblit.frag.0.spirv Binary files differnew file mode 100644 index 0000000..c2a88d2 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.frag.0.spirv diff --git a/vendor/gioui.org/shader/gio/zblit.frag.1.dxbc b/vendor/gioui.org/shader/gio/zblit.frag.1.dxbc Binary files differnew file mode 100644 index 0000000..ddb8dad --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.frag.1.dxbc diff --git a/vendor/gioui.org/shader/gio/zblit.frag.1.glsl100es b/vendor/gioui.org/shader/gio/zblit.frag.1.glsl100es new file mode 100644 index 0000000..ccc6d2a --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.frag.1.glsl100es @@ -0,0 +1,19 @@ +#version 100 +precision mediump float; +precision highp int; + +struct Gradient +{ + vec4 color1; + vec4 color2; +}; + +uniform Gradient _gradient; + +varying highp vec2 vUV; + +void main() +{ + gl_FragData[0] = mix(_gradient.color1, _gradient.color2, vec4(clamp(vUV.x, 0.0, 1.0))); +} + diff --git a/vendor/gioui.org/shader/gio/zblit.frag.1.glsl150 b/vendor/gioui.org/shader/gio/zblit.frag.1.glsl150 new file mode 100644 index 0000000..a55f29f --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.frag.1.glsl150 @@ -0,0 +1,18 @@ +#version 150 + +struct Gradient +{ + vec4 color1; + vec4 color2; +}; + +uniform Gradient _gradient; + +out vec4 fragColor; +in vec2 vUV; + +void main() +{ + fragColor = mix(_gradient.color1, _gradient.color2, vec4(clamp(vUV.x, 0.0, 1.0))); +} + diff --git a/vendor/gioui.org/shader/gio/zblit.frag.1.metallibios b/vendor/gioui.org/shader/gio/zblit.frag.1.metallibios Binary files differnew file mode 100644 index 0000000..680c495 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.frag.1.metallibios diff --git a/vendor/gioui.org/shader/gio/zblit.frag.1.metallibiossimulator b/vendor/gioui.org/shader/gio/zblit.frag.1.metallibiossimulator Binary files differnew file mode 100644 index 0000000..ef5ac86 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.frag.1.metallibiossimulator diff --git a/vendor/gioui.org/shader/gio/zblit.frag.1.metallibmacos b/vendor/gioui.org/shader/gio/zblit.frag.1.metallibmacos Binary files differnew file mode 100644 index 0000000..5fbd2c2 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.frag.1.metallibmacos diff --git a/vendor/gioui.org/shader/gio/zblit.frag.1.spirv b/vendor/gioui.org/shader/gio/zblit.frag.1.spirv Binary files differnew file mode 100644 index 0000000..88128fc --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.frag.1.spirv diff --git a/vendor/gioui.org/shader/gio/zblit.frag.2.dxbc b/vendor/gioui.org/shader/gio/zblit.frag.2.dxbc Binary files differnew file mode 100644 index 0000000..fd95db8 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.frag.2.dxbc diff --git a/vendor/gioui.org/shader/gio/zblit.frag.2.glsl100es b/vendor/gioui.org/shader/gio/zblit.frag.2.glsl100es new file mode 100644 index 0000000..5c2d832 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.frag.2.glsl100es @@ -0,0 +1,13 @@ +#version 100 +precision mediump float; +precision highp int; + +uniform mediump sampler2D tex; + +varying highp vec2 vUV; + +void main() +{ + gl_FragData[0] = texture2D(tex, vUV); +} + diff --git a/vendor/gioui.org/shader/gio/zblit.frag.2.glsl150 b/vendor/gioui.org/shader/gio/zblit.frag.2.glsl150 new file mode 100644 index 0000000..f5df869 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.frag.2.glsl150 @@ -0,0 +1,12 @@ +#version 150 + +uniform sampler2D tex; + +out vec4 fragColor; +in vec2 vUV; + +void main() +{ + fragColor = texture(tex, vUV); +} + diff --git a/vendor/gioui.org/shader/gio/zblit.frag.2.metallibios b/vendor/gioui.org/shader/gio/zblit.frag.2.metallibios Binary files differnew file mode 100644 index 0000000..c4eb922 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.frag.2.metallibios diff --git a/vendor/gioui.org/shader/gio/zblit.frag.2.metallibiossimulator b/vendor/gioui.org/shader/gio/zblit.frag.2.metallibiossimulator Binary files differnew file mode 100644 index 0000000..81f7aa3 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.frag.2.metallibiossimulator diff --git a/vendor/gioui.org/shader/gio/zblit.frag.2.metallibmacos b/vendor/gioui.org/shader/gio/zblit.frag.2.metallibmacos Binary files differnew file mode 100644 index 0000000..70b3da8 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.frag.2.metallibmacos diff --git a/vendor/gioui.org/shader/gio/zblit.frag.2.spirv b/vendor/gioui.org/shader/gio/zblit.frag.2.spirv Binary files differnew file mode 100644 index 0000000..efa380b --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.frag.2.spirv diff --git a/vendor/gioui.org/shader/gio/zblit.vert.0.dxbc b/vendor/gioui.org/shader/gio/zblit.vert.0.dxbc Binary files differnew file mode 100644 index 0000000..5ad9601 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.vert.0.dxbc diff --git a/vendor/gioui.org/shader/gio/zblit.vert.0.glsl100es b/vendor/gioui.org/shader/gio/zblit.vert.0.glsl100es new file mode 100644 index 0000000..61dee04 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.vert.0.glsl100es @@ -0,0 +1,37 @@ +#version 100 + +struct m3x2 +{ + vec3 r0; + vec3 r1; +}; + +struct Block +{ + vec4 transform; + vec4 uvTransformR1; + vec4 uvTransformR2; +}; + +uniform Block _block; + +attribute vec2 pos; +varying vec2 vUV; +attribute vec2 uv; + +vec3 transform3x2(m3x2 t, vec3 v) +{ + return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v)); +} + +void main() +{ + vec2 p = (pos * _block.transform.xy) + _block.transform.zw; + m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0)); + vec3 param_1 = vec3(p, 0.0); + gl_Position = vec4(transform3x2(param, param_1), 1.0); + m3x2 param_2 = m3x2(_block.uvTransformR1.xyz, _block.uvTransformR2.xyz); + vec3 param_3 = vec3(uv, 1.0); + vUV = transform3x2(param_2, param_3).xy; +} + diff --git a/vendor/gioui.org/shader/gio/zblit.vert.0.glsl150 b/vendor/gioui.org/shader/gio/zblit.vert.0.glsl150 new file mode 100644 index 0000000..eba9f1a --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.vert.0.glsl150 @@ -0,0 +1,37 @@ +#version 150 + +struct m3x2 +{ + vec3 r0; + vec3 r1; +}; + +struct Block +{ + vec4 transform; + vec4 uvTransformR1; + vec4 uvTransformR2; +}; + +uniform Block _block; + +in vec2 pos; +out vec2 vUV; +in vec2 uv; + +vec3 transform3x2(m3x2 t, vec3 v) +{ + return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v)); +} + +void main() +{ + vec2 p = (pos * _block.transform.xy) + _block.transform.zw; + m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0)); + vec3 param_1 = vec3(p, 0.0); + gl_Position = vec4(transform3x2(param, param_1), 1.0); + m3x2 param_2 = m3x2(_block.uvTransformR1.xyz, _block.uvTransformR2.xyz); + vec3 param_3 = vec3(uv, 1.0); + vUV = transform3x2(param_2, param_3).xy; +} + diff --git a/vendor/gioui.org/shader/gio/zblit.vert.0.metallibios b/vendor/gioui.org/shader/gio/zblit.vert.0.metallibios Binary files differnew file mode 100644 index 0000000..2450ec9 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.vert.0.metallibios diff --git a/vendor/gioui.org/shader/gio/zblit.vert.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zblit.vert.0.metallibiossimulator Binary files differnew file mode 100644 index 0000000..6b4960b --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.vert.0.metallibiossimulator diff --git a/vendor/gioui.org/shader/gio/zblit.vert.0.metallibmacos b/vendor/gioui.org/shader/gio/zblit.vert.0.metallibmacos Binary files differnew file mode 100644 index 0000000..d902b36 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.vert.0.metallibmacos diff --git a/vendor/gioui.org/shader/gio/zblit.vert.0.spirv b/vendor/gioui.org/shader/gio/zblit.vert.0.spirv Binary files differnew file mode 100644 index 0000000..4f762bb --- /dev/null +++ b/vendor/gioui.org/shader/gio/zblit.vert.0.spirv diff --git a/vendor/gioui.org/shader/gio/zcopy.frag.0.dxbc b/vendor/gioui.org/shader/gio/zcopy.frag.0.dxbc Binary files differnew file mode 100644 index 0000000..a521803 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcopy.frag.0.dxbc diff --git a/vendor/gioui.org/shader/gio/zcopy.frag.0.glsl100es b/vendor/gioui.org/shader/gio/zcopy.frag.0.glsl100es new file mode 100644 index 0000000..b2dd95a --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcopy.frag.0.glsl100es @@ -0,0 +1,27 @@ +#version 100 +precision mediump float; +precision highp int; + +uniform mediump sampler2D tex; + +varying highp vec2 vUV; + +vec3 sRGBtoRGB(vec3 rgb) +{ + bvec3 cutoff = greaterThanEqual(rgb, vec3(0.040449999272823333740234375)); + vec3 below = rgb / vec3(12.9200000762939453125); + vec3 above = pow((rgb + vec3(0.054999999701976776123046875)) / vec3(1.05499994754791259765625), vec3(2.400000095367431640625)); + return vec3(cutoff.x ? above.x : below.x, cutoff.y ? above.y : below.y, cutoff.z ? above.z : below.z); +} + +void main() +{ + vec4 texel = texture2D(tex, vUV); + vec3 param = texel.xyz; + vec3 _59 = sRGBtoRGB(param); + texel.x = _59.x; + texel.y = _59.y; + texel.z = _59.z; + gl_FragData[0] = texel; +} + diff --git a/vendor/gioui.org/shader/gio/zcopy.frag.0.glsl150 b/vendor/gioui.org/shader/gio/zcopy.frag.0.glsl150 new file mode 100644 index 0000000..799add1 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcopy.frag.0.glsl150 @@ -0,0 +1,26 @@ +#version 150 + +uniform sampler2D tex; + +in vec2 vUV; +out vec4 fragColor; + +vec3 sRGBtoRGB(vec3 rgb) +{ + bvec3 cutoff = greaterThanEqual(rgb, vec3(0.040449999272823333740234375)); + vec3 below = rgb / vec3(12.9200000762939453125); + vec3 above = pow((rgb + vec3(0.054999999701976776123046875)) / vec3(1.05499994754791259765625), vec3(2.400000095367431640625)); + return vec3(cutoff.x ? above.x : below.x, cutoff.y ? above.y : below.y, cutoff.z ? above.z : below.z); +} + +void main() +{ + vec4 texel = texture(tex, vUV); + vec3 param = texel.xyz; + vec3 _59 = sRGBtoRGB(param); + texel.x = _59.x; + texel.y = _59.y; + texel.z = _59.z; + fragColor = texel; +} + diff --git a/vendor/gioui.org/shader/gio/zcopy.frag.0.metallibios b/vendor/gioui.org/shader/gio/zcopy.frag.0.metallibios Binary files differnew file mode 100644 index 0000000..f143069 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcopy.frag.0.metallibios diff --git a/vendor/gioui.org/shader/gio/zcopy.frag.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zcopy.frag.0.metallibiossimulator Binary files differnew file mode 100644 index 0000000..8fe4c74 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcopy.frag.0.metallibiossimulator diff --git a/vendor/gioui.org/shader/gio/zcopy.frag.0.metallibmacos b/vendor/gioui.org/shader/gio/zcopy.frag.0.metallibmacos Binary files differnew file mode 100644 index 0000000..2e43803 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcopy.frag.0.metallibmacos diff --git a/vendor/gioui.org/shader/gio/zcopy.frag.0.spirv b/vendor/gioui.org/shader/gio/zcopy.frag.0.spirv Binary files differnew file mode 100644 index 0000000..3299552 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcopy.frag.0.spirv diff --git a/vendor/gioui.org/shader/gio/zcopy.vert.0.dxbc b/vendor/gioui.org/shader/gio/zcopy.vert.0.dxbc Binary files differnew file mode 100644 index 0000000..acce7ab --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcopy.vert.0.dxbc diff --git a/vendor/gioui.org/shader/gio/zcopy.vert.0.glsl100es b/vendor/gioui.org/shader/gio/zcopy.vert.0.glsl100es new file mode 100644 index 0000000..ff0be47 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcopy.vert.0.glsl100es @@ -0,0 +1,35 @@ +#version 100 + +struct m3x2 +{ + vec3 r0; + vec3 r1; +}; + +struct Block +{ + vec2 scale; + vec2 pos; + vec2 uvScale; +}; + +uniform Block _block; + +varying vec2 vUV; +attribute vec2 uv; +attribute vec2 pos; + +vec3 transform3x2(m3x2 t, vec3 v) +{ + return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v)); +} + +void main() +{ + vUV = vec2(uv * _block.uvScale); + vec2 p = vec2((pos * _block.scale) + _block.pos); + m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0)); + vec3 param_1 = vec3(p, 0.0); + gl_Position = vec4(transform3x2(param, param_1), 1.0); +} + diff --git a/vendor/gioui.org/shader/gio/zcopy.vert.0.glsl150 b/vendor/gioui.org/shader/gio/zcopy.vert.0.glsl150 new file mode 100644 index 0000000..036cd39 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcopy.vert.0.glsl150 @@ -0,0 +1,35 @@ +#version 150 + +struct m3x2 +{ + vec3 r0; + vec3 r1; +}; + +struct Block +{ + vec2 scale; + vec2 pos; + vec2 uvScale; +}; + +uniform Block _block; + +out vec2 vUV; +in vec2 uv; +in vec2 pos; + +vec3 transform3x2(m3x2 t, vec3 v) +{ + return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v)); +} + +void main() +{ + vUV = vec2(uv * _block.uvScale); + vec2 p = vec2((pos * _block.scale) + _block.pos); + m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0)); + vec3 param_1 = vec3(p, 0.0); + gl_Position = vec4(transform3x2(param, param_1), 1.0); +} + diff --git a/vendor/gioui.org/shader/gio/zcopy.vert.0.metallibios b/vendor/gioui.org/shader/gio/zcopy.vert.0.metallibios Binary files differnew file mode 100644 index 0000000..25f5e56 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcopy.vert.0.metallibios diff --git a/vendor/gioui.org/shader/gio/zcopy.vert.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zcopy.vert.0.metallibiossimulator Binary files differnew file mode 100644 index 0000000..f696caf --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcopy.vert.0.metallibiossimulator diff --git a/vendor/gioui.org/shader/gio/zcopy.vert.0.metallibmacos b/vendor/gioui.org/shader/gio/zcopy.vert.0.metallibmacos Binary files differnew file mode 100644 index 0000000..d4a5777 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcopy.vert.0.metallibmacos diff --git a/vendor/gioui.org/shader/gio/zcopy.vert.0.spirv b/vendor/gioui.org/shader/gio/zcopy.vert.0.spirv Binary files differnew file mode 100644 index 0000000..7251754 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcopy.vert.0.spirv diff --git a/vendor/gioui.org/shader/gio/zcover.frag.0.dxbc b/vendor/gioui.org/shader/gio/zcover.frag.0.dxbc Binary files differnew file mode 100644 index 0000000..8b77ae4 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.frag.0.dxbc diff --git a/vendor/gioui.org/shader/gio/zcover.frag.0.glsl100es b/vendor/gioui.org/shader/gio/zcover.frag.0.glsl100es new file mode 100644 index 0000000..2399901 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.frag.0.glsl100es @@ -0,0 +1,23 @@ +#version 100 +precision mediump float; +precision highp int; + +struct Color +{ + vec4 color; +}; + +uniform Color _color; + +uniform mediump sampler2D cover; + +varying highp vec2 vCoverUV; +varying highp vec2 vUV; + +void main() +{ + gl_FragData[0] = _color.color; + float c = min(abs(texture2D(cover, vCoverUV).x), 1.0); + gl_FragData[0] *= c; +} + diff --git a/vendor/gioui.org/shader/gio/zcover.frag.0.glsl150 b/vendor/gioui.org/shader/gio/zcover.frag.0.glsl150 new file mode 100644 index 0000000..ce1beaa --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.frag.0.glsl150 @@ -0,0 +1,22 @@ +#version 150 + +struct Color +{ + vec4 color; +}; + +uniform Color _color; + +uniform sampler2D cover; + +out vec4 fragColor; +in vec2 vCoverUV; +in vec2 vUV; + +void main() +{ + fragColor = _color.color; + float c = min(abs(texture(cover, vCoverUV).x), 1.0); + fragColor *= c; +} + diff --git a/vendor/gioui.org/shader/gio/zcover.frag.0.metallibios b/vendor/gioui.org/shader/gio/zcover.frag.0.metallibios Binary files differnew file mode 100644 index 0000000..22256c8 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.frag.0.metallibios diff --git a/vendor/gioui.org/shader/gio/zcover.frag.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zcover.frag.0.metallibiossimulator Binary files differnew file mode 100644 index 0000000..c8040ad --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.frag.0.metallibiossimulator diff --git a/vendor/gioui.org/shader/gio/zcover.frag.0.metallibmacos b/vendor/gioui.org/shader/gio/zcover.frag.0.metallibmacos Binary files differnew file mode 100644 index 0000000..fe25160 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.frag.0.metallibmacos diff --git a/vendor/gioui.org/shader/gio/zcover.frag.0.spirv b/vendor/gioui.org/shader/gio/zcover.frag.0.spirv Binary files differnew file mode 100644 index 0000000..2061ff1 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.frag.0.spirv diff --git a/vendor/gioui.org/shader/gio/zcover.frag.1.dxbc b/vendor/gioui.org/shader/gio/zcover.frag.1.dxbc Binary files differnew file mode 100644 index 0000000..c9a44cf --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.frag.1.dxbc diff --git a/vendor/gioui.org/shader/gio/zcover.frag.1.glsl100es b/vendor/gioui.org/shader/gio/zcover.frag.1.glsl100es new file mode 100644 index 0000000..01cc88e --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.frag.1.glsl100es @@ -0,0 +1,24 @@ +#version 100 +precision mediump float; +precision highp int; + +struct Gradient +{ + vec4 color1; + vec4 color2; +}; + +uniform Gradient _gradient; + +uniform mediump sampler2D cover; + +varying highp vec2 vUV; +varying highp vec2 vCoverUV; + +void main() +{ + gl_FragData[0] = mix(_gradient.color1, _gradient.color2, vec4(clamp(vUV.x, 0.0, 1.0))); + float c = min(abs(texture2D(cover, vCoverUV).x), 1.0); + gl_FragData[0] *= c; +} + diff --git a/vendor/gioui.org/shader/gio/zcover.frag.1.glsl150 b/vendor/gioui.org/shader/gio/zcover.frag.1.glsl150 new file mode 100644 index 0000000..3f832c3 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.frag.1.glsl150 @@ -0,0 +1,23 @@ +#version 150 + +struct Gradient +{ + vec4 color1; + vec4 color2; +}; + +uniform Gradient _gradient; + +uniform sampler2D cover; + +out vec4 fragColor; +in vec2 vUV; +in vec2 vCoverUV; + +void main() +{ + fragColor = mix(_gradient.color1, _gradient.color2, vec4(clamp(vUV.x, 0.0, 1.0))); + float c = min(abs(texture(cover, vCoverUV).x), 1.0); + fragColor *= c; +} + diff --git a/vendor/gioui.org/shader/gio/zcover.frag.1.metallibios b/vendor/gioui.org/shader/gio/zcover.frag.1.metallibios Binary files differnew file mode 100644 index 0000000..228996a --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.frag.1.metallibios diff --git a/vendor/gioui.org/shader/gio/zcover.frag.1.metallibiossimulator b/vendor/gioui.org/shader/gio/zcover.frag.1.metallibiossimulator Binary files differnew file mode 100644 index 0000000..1c99553 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.frag.1.metallibiossimulator diff --git a/vendor/gioui.org/shader/gio/zcover.frag.1.metallibmacos b/vendor/gioui.org/shader/gio/zcover.frag.1.metallibmacos Binary files differnew file mode 100644 index 0000000..889e249 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.frag.1.metallibmacos diff --git a/vendor/gioui.org/shader/gio/zcover.frag.1.spirv b/vendor/gioui.org/shader/gio/zcover.frag.1.spirv Binary files differnew file mode 100644 index 0000000..4c48d20 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.frag.1.spirv diff --git a/vendor/gioui.org/shader/gio/zcover.frag.2.dxbc b/vendor/gioui.org/shader/gio/zcover.frag.2.dxbc Binary files differnew file mode 100644 index 0000000..d8c6a80 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.frag.2.dxbc diff --git a/vendor/gioui.org/shader/gio/zcover.frag.2.glsl100es b/vendor/gioui.org/shader/gio/zcover.frag.2.glsl100es new file mode 100644 index 0000000..7cfde4b --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.frag.2.glsl100es @@ -0,0 +1,17 @@ +#version 100 +precision mediump float; +precision highp int; + +uniform mediump sampler2D tex; +uniform mediump sampler2D cover; + +varying highp vec2 vUV; +varying highp vec2 vCoverUV; + +void main() +{ + gl_FragData[0] = texture2D(tex, vUV); + float c = min(abs(texture2D(cover, vCoverUV).x), 1.0); + gl_FragData[0] *= c; +} + diff --git a/vendor/gioui.org/shader/gio/zcover.frag.2.glsl150 b/vendor/gioui.org/shader/gio/zcover.frag.2.glsl150 new file mode 100644 index 0000000..939baee --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.frag.2.glsl150 @@ -0,0 +1,16 @@ +#version 150 + +uniform sampler2D tex; +uniform sampler2D cover; + +out vec4 fragColor; +in vec2 vUV; +in vec2 vCoverUV; + +void main() +{ + fragColor = texture(tex, vUV); + float c = min(abs(texture(cover, vCoverUV).x), 1.0); + fragColor *= c; +} + diff --git a/vendor/gioui.org/shader/gio/zcover.frag.2.metallibios b/vendor/gioui.org/shader/gio/zcover.frag.2.metallibios Binary files differnew file mode 100644 index 0000000..6eba400 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.frag.2.metallibios diff --git a/vendor/gioui.org/shader/gio/zcover.frag.2.metallibiossimulator b/vendor/gioui.org/shader/gio/zcover.frag.2.metallibiossimulator Binary files differnew file mode 100644 index 0000000..09ddb1f --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.frag.2.metallibiossimulator diff --git a/vendor/gioui.org/shader/gio/zcover.frag.2.metallibmacos b/vendor/gioui.org/shader/gio/zcover.frag.2.metallibmacos Binary files differnew file mode 100644 index 0000000..3ba9105 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.frag.2.metallibmacos diff --git a/vendor/gioui.org/shader/gio/zcover.frag.2.spirv b/vendor/gioui.org/shader/gio/zcover.frag.2.spirv Binary files differnew file mode 100644 index 0000000..cd618ce --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.frag.2.spirv diff --git a/vendor/gioui.org/shader/gio/zcover.vert.0.dxbc b/vendor/gioui.org/shader/gio/zcover.vert.0.dxbc Binary files differnew file mode 100644 index 0000000..3b6cf6b --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.vert.0.dxbc diff --git a/vendor/gioui.org/shader/gio/zcover.vert.0.glsl100es b/vendor/gioui.org/shader/gio/zcover.vert.0.glsl100es new file mode 100644 index 0000000..423ca60 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.vert.0.glsl100es @@ -0,0 +1,41 @@ +#version 100 + +struct m3x2 +{ + vec3 r0; + vec3 r1; +}; + +struct Block +{ + vec4 transform; + vec4 uvCoverTransform; + vec4 uvTransformR1; + vec4 uvTransformR2; +}; + +uniform Block _block; + +attribute vec2 pos; +varying vec2 vUV; +attribute vec2 uv; +varying vec2 vCoverUV; + +vec3 transform3x2(m3x2 t, vec3 v) +{ + return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v)); +} + +void main() +{ + vec2 p = vec2((pos * _block.transform.xy) + _block.transform.zw); + m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0)); + vec3 param_1 = vec3(p, 0.0); + gl_Position = vec4(transform3x2(param, param_1), 1.0); + m3x2 param_2 = m3x2(_block.uvTransformR1.xyz, _block.uvTransformR2.xyz); + vec3 param_3 = vec3(uv, 1.0); + vUV = transform3x2(param_2, param_3).xy; + vec3 uv3 = vec3(uv, 1.0); + vCoverUV = ((uv3 * vec3(_block.uvCoverTransform.xy, 1.0)) + vec3(_block.uvCoverTransform.zw, 0.0)).xy; +} + diff --git a/vendor/gioui.org/shader/gio/zcover.vert.0.glsl150 b/vendor/gioui.org/shader/gio/zcover.vert.0.glsl150 new file mode 100644 index 0000000..a5005d6 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.vert.0.glsl150 @@ -0,0 +1,41 @@ +#version 150 + +struct m3x2 +{ + vec3 r0; + vec3 r1; +}; + +struct Block +{ + vec4 transform; + vec4 uvCoverTransform; + vec4 uvTransformR1; + vec4 uvTransformR2; +}; + +uniform Block _block; + +in vec2 pos; +out vec2 vUV; +in vec2 uv; +out vec2 vCoverUV; + +vec3 transform3x2(m3x2 t, vec3 v) +{ + return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v)); +} + +void main() +{ + vec2 p = vec2((pos * _block.transform.xy) + _block.transform.zw); + m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0)); + vec3 param_1 = vec3(p, 0.0); + gl_Position = vec4(transform3x2(param, param_1), 1.0); + m3x2 param_2 = m3x2(_block.uvTransformR1.xyz, _block.uvTransformR2.xyz); + vec3 param_3 = vec3(uv, 1.0); + vUV = transform3x2(param_2, param_3).xy; + vec3 uv3 = vec3(uv, 1.0); + vCoverUV = ((uv3 * vec3(_block.uvCoverTransform.xy, 1.0)) + vec3(_block.uvCoverTransform.zw, 0.0)).xy; +} + diff --git a/vendor/gioui.org/shader/gio/zcover.vert.0.metallibios b/vendor/gioui.org/shader/gio/zcover.vert.0.metallibios Binary files differnew file mode 100644 index 0000000..335704b --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.vert.0.metallibios diff --git a/vendor/gioui.org/shader/gio/zcover.vert.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zcover.vert.0.metallibiossimulator Binary files differnew file mode 100644 index 0000000..96470cd --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.vert.0.metallibiossimulator diff --git a/vendor/gioui.org/shader/gio/zcover.vert.0.metallibmacos b/vendor/gioui.org/shader/gio/zcover.vert.0.metallibmacos Binary files differnew file mode 100644 index 0000000..629e82b --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.vert.0.metallibmacos diff --git a/vendor/gioui.org/shader/gio/zcover.vert.0.spirv b/vendor/gioui.org/shader/gio/zcover.vert.0.spirv Binary files differnew file mode 100644 index 0000000..f9af8ab --- /dev/null +++ b/vendor/gioui.org/shader/gio/zcover.vert.0.spirv diff --git a/vendor/gioui.org/shader/gio/zinput.vert.0.dxbc b/vendor/gioui.org/shader/gio/zinput.vert.0.dxbc Binary files differnew file mode 100644 index 0000000..096bd01 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zinput.vert.0.dxbc diff --git a/vendor/gioui.org/shader/gio/zinput.vert.0.glsl100es b/vendor/gioui.org/shader/gio/zinput.vert.0.glsl100es new file mode 100644 index 0000000..d1d4c8d --- /dev/null +++ b/vendor/gioui.org/shader/gio/zinput.vert.0.glsl100es @@ -0,0 +1,22 @@ +#version 100 + +struct m3x2 +{ + vec3 r0; + vec3 r1; +}; + +attribute vec4 position; + +vec3 transform3x2(m3x2 t, vec3 v) +{ + return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v)); +} + +void main() +{ + m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0)); + vec3 param_1 = position.xyz; + gl_Position = vec4(transform3x2(param, param_1), position.w); +} + diff --git a/vendor/gioui.org/shader/gio/zinput.vert.0.glsl150 b/vendor/gioui.org/shader/gio/zinput.vert.0.glsl150 new file mode 100644 index 0000000..ab28308 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zinput.vert.0.glsl150 @@ -0,0 +1,22 @@ +#version 150 + +struct m3x2 +{ + vec3 r0; + vec3 r1; +}; + +in vec4 position; + +vec3 transform3x2(m3x2 t, vec3 v) +{ + return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v)); +} + +void main() +{ + m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, -1.0, 0.0)); + vec3 param_1 = position.xyz; + gl_Position = vec4(transform3x2(param, param_1), position.w); +} + diff --git a/vendor/gioui.org/shader/gio/zinput.vert.0.metallibios b/vendor/gioui.org/shader/gio/zinput.vert.0.metallibios Binary files differnew file mode 100644 index 0000000..6fdc427 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zinput.vert.0.metallibios diff --git a/vendor/gioui.org/shader/gio/zinput.vert.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zinput.vert.0.metallibiossimulator Binary files differnew file mode 100644 index 0000000..da28890 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zinput.vert.0.metallibiossimulator diff --git a/vendor/gioui.org/shader/gio/zinput.vert.0.metallibmacos b/vendor/gioui.org/shader/gio/zinput.vert.0.metallibmacos Binary files differnew file mode 100644 index 0000000..064c501 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zinput.vert.0.metallibmacos diff --git a/vendor/gioui.org/shader/gio/zinput.vert.0.spirv b/vendor/gioui.org/shader/gio/zinput.vert.0.spirv Binary files differnew file mode 100644 index 0000000..eb999e5 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zinput.vert.0.spirv diff --git a/vendor/gioui.org/shader/gio/zintersect.frag.0.dxbc b/vendor/gioui.org/shader/gio/zintersect.frag.0.dxbc Binary files differnew file mode 100644 index 0000000..f2081ee --- /dev/null +++ b/vendor/gioui.org/shader/gio/zintersect.frag.0.dxbc diff --git a/vendor/gioui.org/shader/gio/zintersect.frag.0.glsl100es b/vendor/gioui.org/shader/gio/zintersect.frag.0.glsl100es new file mode 100644 index 0000000..90fe2f9 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zintersect.frag.0.glsl100es @@ -0,0 +1,13 @@ +#version 100 +precision mediump float; +precision highp int; + +uniform mediump sampler2D cover; + +varying highp vec2 vUV; + +void main() +{ + gl_FragData[0].x = abs(texture2D(cover, vUV).x); +} + diff --git a/vendor/gioui.org/shader/gio/zintersect.frag.0.glsl150 b/vendor/gioui.org/shader/gio/zintersect.frag.0.glsl150 new file mode 100644 index 0000000..f574aae --- /dev/null +++ b/vendor/gioui.org/shader/gio/zintersect.frag.0.glsl150 @@ -0,0 +1,12 @@ +#version 150 + +uniform sampler2D cover; + +out vec4 fragColor; +in vec2 vUV; + +void main() +{ + fragColor.x = abs(texture(cover, vUV).x); +} + diff --git a/vendor/gioui.org/shader/gio/zintersect.frag.0.metallibios b/vendor/gioui.org/shader/gio/zintersect.frag.0.metallibios Binary files differnew file mode 100644 index 0000000..f48da4e --- /dev/null +++ b/vendor/gioui.org/shader/gio/zintersect.frag.0.metallibios diff --git a/vendor/gioui.org/shader/gio/zintersect.frag.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zintersect.frag.0.metallibiossimulator Binary files differnew file mode 100644 index 0000000..b1a40c4 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zintersect.frag.0.metallibiossimulator diff --git a/vendor/gioui.org/shader/gio/zintersect.frag.0.metallibmacos b/vendor/gioui.org/shader/gio/zintersect.frag.0.metallibmacos Binary files differnew file mode 100644 index 0000000..f16d190 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zintersect.frag.0.metallibmacos diff --git a/vendor/gioui.org/shader/gio/zintersect.frag.0.spirv b/vendor/gioui.org/shader/gio/zintersect.frag.0.spirv Binary files differnew file mode 100644 index 0000000..6c28644 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zintersect.frag.0.spirv diff --git a/vendor/gioui.org/shader/gio/zintersect.vert.0.dxbc b/vendor/gioui.org/shader/gio/zintersect.vert.0.dxbc Binary files differnew file mode 100644 index 0000000..03005ea --- /dev/null +++ b/vendor/gioui.org/shader/gio/zintersect.vert.0.dxbc diff --git a/vendor/gioui.org/shader/gio/zintersect.vert.0.glsl100es b/vendor/gioui.org/shader/gio/zintersect.vert.0.glsl100es new file mode 100644 index 0000000..4441763 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zintersect.vert.0.glsl100es @@ -0,0 +1,35 @@ +#version 100 + +struct m3x2 +{ + vec3 r0; + vec3 r1; +}; + +struct Block +{ + vec4 uvTransform; + vec4 subUVTransform; +}; + +uniform Block _block; + +attribute vec2 pos; +varying vec2 vUV; +attribute vec2 uv; + +vec3 transform3x2(m3x2 t, vec3 v) +{ + return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v)); +} + +void main() +{ + m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0)); + vec3 param_1 = vec3(pos, 1.0); + vec3 p = transform3x2(param, param_1); + gl_Position = vec4(p, 1.0); + vUV = (uv * _block.subUVTransform.xy) + _block.subUVTransform.zw; + vUV = (vUV * _block.uvTransform.xy) + _block.uvTransform.zw; +} + diff --git a/vendor/gioui.org/shader/gio/zintersect.vert.0.glsl150 b/vendor/gioui.org/shader/gio/zintersect.vert.0.glsl150 new file mode 100644 index 0000000..656b4c9 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zintersect.vert.0.glsl150 @@ -0,0 +1,35 @@ +#version 150 + +struct m3x2 +{ + vec3 r0; + vec3 r1; +}; + +struct Block +{ + vec4 uvTransform; + vec4 subUVTransform; +}; + +uniform Block _block; + +in vec2 pos; +out vec2 vUV; +in vec2 uv; + +vec3 transform3x2(m3x2 t, vec3 v) +{ + return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v)); +} + +void main() +{ + m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0)); + vec3 param_1 = vec3(pos, 1.0); + vec3 p = transform3x2(param, param_1); + gl_Position = vec4(p, 1.0); + vUV = (uv * _block.subUVTransform.xy) + _block.subUVTransform.zw; + vUV = (vUV * _block.uvTransform.xy) + _block.uvTransform.zw; +} + diff --git a/vendor/gioui.org/shader/gio/zintersect.vert.0.metallibios b/vendor/gioui.org/shader/gio/zintersect.vert.0.metallibios Binary files differnew file mode 100644 index 0000000..975fbb8 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zintersect.vert.0.metallibios diff --git a/vendor/gioui.org/shader/gio/zintersect.vert.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zintersect.vert.0.metallibiossimulator Binary files differnew file mode 100644 index 0000000..682bad1 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zintersect.vert.0.metallibiossimulator diff --git a/vendor/gioui.org/shader/gio/zintersect.vert.0.metallibmacos b/vendor/gioui.org/shader/gio/zintersect.vert.0.metallibmacos Binary files differnew file mode 100644 index 0000000..a43236d --- /dev/null +++ b/vendor/gioui.org/shader/gio/zintersect.vert.0.metallibmacos diff --git a/vendor/gioui.org/shader/gio/zintersect.vert.0.spirv b/vendor/gioui.org/shader/gio/zintersect.vert.0.spirv Binary files differnew file mode 100644 index 0000000..13e7335 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zintersect.vert.0.spirv diff --git a/vendor/gioui.org/shader/gio/zmaterial.frag.0.dxbc b/vendor/gioui.org/shader/gio/zmaterial.frag.0.dxbc Binary files differnew file mode 100644 index 0000000..e250378 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zmaterial.frag.0.dxbc diff --git a/vendor/gioui.org/shader/gio/zmaterial.frag.0.glsl100es b/vendor/gioui.org/shader/gio/zmaterial.frag.0.glsl100es new file mode 100644 index 0000000..f2f2a30 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zmaterial.frag.0.glsl100es @@ -0,0 +1,37 @@ +#version 100 +precision mediump float; +precision highp int; + +struct Color +{ + float emulateSRGB; +}; + +uniform Color _color; + +uniform mediump sampler2D tex; + +varying highp vec2 vUV; + +vec3 RGBtosRGB(vec3 rgb) +{ + bvec3 cutoff = greaterThanEqual(rgb, vec3(0.003130800090730190277099609375)); + vec3 below = vec3(12.9200000762939453125) * rgb; + vec3 above = (vec3(1.05499994754791259765625) * pow(rgb, vec3(0.416660010814666748046875))) - vec3(0.054999999701976776123046875); + return vec3(cutoff.x ? above.x : below.x, cutoff.y ? above.y : below.y, cutoff.z ? above.z : below.z); +} + +void main() +{ + vec4 texel = texture2D(tex, vUV); + if (_color.emulateSRGB == 0.0) + { + vec3 param = texel.xyz; + vec3 _71 = RGBtosRGB(param); + texel.x = _71.x; + texel.y = _71.y; + texel.z = _71.z; + } + gl_FragData[0] = texel; +} + diff --git a/vendor/gioui.org/shader/gio/zmaterial.frag.0.glsl150 b/vendor/gioui.org/shader/gio/zmaterial.frag.0.glsl150 new file mode 100644 index 0000000..6b8bed3 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zmaterial.frag.0.glsl150 @@ -0,0 +1,36 @@ +#version 150 + +struct Color +{ + float emulateSRGB; +}; + +uniform Color _color; + +uniform sampler2D tex; + +in vec2 vUV; +out vec4 fragColor; + +vec3 RGBtosRGB(vec3 rgb) +{ + bvec3 cutoff = greaterThanEqual(rgb, vec3(0.003130800090730190277099609375)); + vec3 below = vec3(12.9200000762939453125) * rgb; + vec3 above = (vec3(1.05499994754791259765625) * pow(rgb, vec3(0.416660010814666748046875))) - vec3(0.054999999701976776123046875); + return vec3(cutoff.x ? above.x : below.x, cutoff.y ? above.y : below.y, cutoff.z ? above.z : below.z); +} + +void main() +{ + vec4 texel = texture(tex, vUV); + if (_color.emulateSRGB == 0.0) + { + vec3 param = texel.xyz; + vec3 _71 = RGBtosRGB(param); + texel.x = _71.x; + texel.y = _71.y; + texel.z = _71.z; + } + fragColor = texel; +} + diff --git a/vendor/gioui.org/shader/gio/zmaterial.frag.0.metallibios b/vendor/gioui.org/shader/gio/zmaterial.frag.0.metallibios Binary files differnew file mode 100644 index 0000000..8679f2a --- /dev/null +++ b/vendor/gioui.org/shader/gio/zmaterial.frag.0.metallibios diff --git a/vendor/gioui.org/shader/gio/zmaterial.frag.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zmaterial.frag.0.metallibiossimulator Binary files differnew file mode 100644 index 0000000..6306d9a --- /dev/null +++ b/vendor/gioui.org/shader/gio/zmaterial.frag.0.metallibiossimulator diff --git a/vendor/gioui.org/shader/gio/zmaterial.frag.0.metallibmacos b/vendor/gioui.org/shader/gio/zmaterial.frag.0.metallibmacos Binary files differnew file mode 100644 index 0000000..911948e --- /dev/null +++ b/vendor/gioui.org/shader/gio/zmaterial.frag.0.metallibmacos diff --git a/vendor/gioui.org/shader/gio/zmaterial.frag.0.spirv b/vendor/gioui.org/shader/gio/zmaterial.frag.0.spirv Binary files differnew file mode 100644 index 0000000..475cfb8 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zmaterial.frag.0.spirv diff --git a/vendor/gioui.org/shader/gio/zmaterial.vert.0.dxbc b/vendor/gioui.org/shader/gio/zmaterial.vert.0.dxbc Binary files differnew file mode 100644 index 0000000..a5056cc --- /dev/null +++ b/vendor/gioui.org/shader/gio/zmaterial.vert.0.dxbc diff --git a/vendor/gioui.org/shader/gio/zmaterial.vert.0.glsl100es b/vendor/gioui.org/shader/gio/zmaterial.vert.0.glsl100es new file mode 100644 index 0000000..39a7b50 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zmaterial.vert.0.glsl100es @@ -0,0 +1,34 @@ +#version 100 + +struct m3x2 +{ + vec3 r0; + vec3 r1; +}; + +struct Block +{ + vec2 scale; + vec2 pos; +}; + +uniform Block _block; + +varying vec2 vUV; +attribute vec2 uv; +attribute vec2 pos; + +vec3 transform3x2(m3x2 t, vec3 v) +{ + return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v)); +} + +void main() +{ + vUV = uv; + vec2 p = vec2((pos * _block.scale) + _block.pos); + m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0)); + vec3 param_1 = vec3(p, 0.0); + gl_Position = vec4(transform3x2(param, param_1), 1.0); +} + diff --git a/vendor/gioui.org/shader/gio/zmaterial.vert.0.glsl150 b/vendor/gioui.org/shader/gio/zmaterial.vert.0.glsl150 new file mode 100644 index 0000000..be02b44 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zmaterial.vert.0.glsl150 @@ -0,0 +1,34 @@ +#version 150 + +struct m3x2 +{ + vec3 r0; + vec3 r1; +}; + +struct Block +{ + vec2 scale; + vec2 pos; +}; + +uniform Block _block; + +out vec2 vUV; +in vec2 uv; +in vec2 pos; + +vec3 transform3x2(m3x2 t, vec3 v) +{ + return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v)); +} + +void main() +{ + vUV = uv; + vec2 p = vec2((pos * _block.scale) + _block.pos); + m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0)); + vec3 param_1 = vec3(p, 0.0); + gl_Position = vec4(transform3x2(param, param_1), 1.0); +} + diff --git a/vendor/gioui.org/shader/gio/zmaterial.vert.0.metallibios b/vendor/gioui.org/shader/gio/zmaterial.vert.0.metallibios Binary files differnew file mode 100644 index 0000000..19cf6d8 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zmaterial.vert.0.metallibios diff --git a/vendor/gioui.org/shader/gio/zmaterial.vert.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zmaterial.vert.0.metallibiossimulator Binary files differnew file mode 100644 index 0000000..c710ca9 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zmaterial.vert.0.metallibiossimulator diff --git a/vendor/gioui.org/shader/gio/zmaterial.vert.0.metallibmacos b/vendor/gioui.org/shader/gio/zmaterial.vert.0.metallibmacos Binary files differnew file mode 100644 index 0000000..cfbdfbc --- /dev/null +++ b/vendor/gioui.org/shader/gio/zmaterial.vert.0.metallibmacos diff --git a/vendor/gioui.org/shader/gio/zmaterial.vert.0.spirv b/vendor/gioui.org/shader/gio/zmaterial.vert.0.spirv Binary files differnew file mode 100644 index 0000000..18e731f --- /dev/null +++ b/vendor/gioui.org/shader/gio/zmaterial.vert.0.spirv diff --git a/vendor/gioui.org/shader/gio/zsimple.frag.0.dxbc b/vendor/gioui.org/shader/gio/zsimple.frag.0.dxbc Binary files differnew file mode 100644 index 0000000..f4f8894 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zsimple.frag.0.dxbc diff --git a/vendor/gioui.org/shader/gio/zsimple.frag.0.glsl100es b/vendor/gioui.org/shader/gio/zsimple.frag.0.glsl100es new file mode 100644 index 0000000..0f86af5 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zsimple.frag.0.glsl100es @@ -0,0 +1,9 @@ +#version 100 +precision mediump float; +precision highp int; + +void main() +{ + gl_FragData[0] = vec4(0.25, 0.550000011920928955078125, 0.75, 1.0); +} + diff --git a/vendor/gioui.org/shader/gio/zsimple.frag.0.glsl150 b/vendor/gioui.org/shader/gio/zsimple.frag.0.glsl150 new file mode 100644 index 0000000..db4c060 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zsimple.frag.0.glsl150 @@ -0,0 +1,9 @@ +#version 150 + +out vec4 fragColor; + +void main() +{ + fragColor = vec4(0.25, 0.550000011920928955078125, 0.75, 1.0); +} + diff --git a/vendor/gioui.org/shader/gio/zsimple.frag.0.metallibios b/vendor/gioui.org/shader/gio/zsimple.frag.0.metallibios Binary files differnew file mode 100644 index 0000000..05e6329 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zsimple.frag.0.metallibios diff --git a/vendor/gioui.org/shader/gio/zsimple.frag.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zsimple.frag.0.metallibiossimulator Binary files differnew file mode 100644 index 0000000..2c87af9 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zsimple.frag.0.metallibiossimulator diff --git a/vendor/gioui.org/shader/gio/zsimple.frag.0.metallibmacos b/vendor/gioui.org/shader/gio/zsimple.frag.0.metallibmacos Binary files differnew file mode 100644 index 0000000..407d7ea --- /dev/null +++ b/vendor/gioui.org/shader/gio/zsimple.frag.0.metallibmacos diff --git a/vendor/gioui.org/shader/gio/zsimple.frag.0.spirv b/vendor/gioui.org/shader/gio/zsimple.frag.0.spirv Binary files differnew file mode 100644 index 0000000..2b2aba3 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zsimple.frag.0.spirv diff --git a/vendor/gioui.org/shader/gio/zstencil.frag.0.dxbc b/vendor/gioui.org/shader/gio/zstencil.frag.0.dxbc Binary files differnew file mode 100644 index 0000000..ded34dc --- /dev/null +++ b/vendor/gioui.org/shader/gio/zstencil.frag.0.dxbc diff --git a/vendor/gioui.org/shader/gio/zstencil.frag.0.glsl100es b/vendor/gioui.org/shader/gio/zstencil.frag.0.glsl100es new file mode 100644 index 0000000..15530e3 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zstencil.frag.0.glsl100es @@ -0,0 +1,38 @@ +#version 100 +precision mediump float; +precision highp int; + +varying highp vec2 vTo; +varying highp vec2 vFrom; +varying highp vec2 vCtrl; + +void main() +{ + float dx = vTo.x - vFrom.x; + bool increasing = vTo.x >= vFrom.x; + bvec2 _35 = bvec2(increasing); + vec2 left = vec2(_35.x ? vFrom.x : vTo.x, _35.y ? vFrom.y : vTo.y); + bvec2 _41 = bvec2(increasing); + vec2 right = vec2(_41.x ? vTo.x : vFrom.x, _41.y ? vTo.y : vFrom.y); + vec2 extent = clamp(vec2(vFrom.x, vTo.x), vec2(-0.5), vec2(0.5)); + float midx = mix(extent.x, extent.y, 0.5); + float x0 = midx - left.x; + vec2 p1 = vCtrl - left; + vec2 v = right - vCtrl; + float t = x0 / (p1.x + sqrt((p1.x * p1.x) + ((v.x - p1.x) * x0))); + float y = mix(mix(left.y, vCtrl.y, t), mix(vCtrl.y, right.y, t), t); + vec2 d_half = mix(p1, v, vec2(t)); + float dy = d_half.y / d_half.x; + float width = extent.y - extent.x; + dy = abs(dy * width); + vec4 sides = vec4((dy * 0.5) + y, (dy * (-0.5)) + y, (0.5 - y) / dy, ((-0.5) - y) / dy); + sides = clamp(sides + vec4(0.5), vec4(0.0), vec4(1.0)); + float area = 0.5 * ((((sides.z - (sides.z * sides.y)) + 1.0) - sides.x) + (sides.x * sides.w)); + area *= width; + if (width == 0.0) + { + area = 0.0; + } + gl_FragData[0].x = area; +} + diff --git a/vendor/gioui.org/shader/gio/zstencil.frag.0.glsl150 b/vendor/gioui.org/shader/gio/zstencil.frag.0.glsl150 new file mode 100644 index 0000000..8e2abd8 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zstencil.frag.0.glsl150 @@ -0,0 +1,37 @@ +#version 150 + +in vec2 vTo; +in vec2 vFrom; +in vec2 vCtrl; +out vec4 fragCover; + +void main() +{ + float dx = vTo.x - vFrom.x; + bool increasing = vTo.x >= vFrom.x; + bvec2 _35 = bvec2(increasing); + vec2 left = vec2(_35.x ? vFrom.x : vTo.x, _35.y ? vFrom.y : vTo.y); + bvec2 _41 = bvec2(increasing); + vec2 right = vec2(_41.x ? vTo.x : vFrom.x, _41.y ? vTo.y : vFrom.y); + vec2 extent = clamp(vec2(vFrom.x, vTo.x), vec2(-0.5), vec2(0.5)); + float midx = mix(extent.x, extent.y, 0.5); + float x0 = midx - left.x; + vec2 p1 = vCtrl - left; + vec2 v = right - vCtrl; + float t = x0 / (p1.x + sqrt((p1.x * p1.x) + ((v.x - p1.x) * x0))); + float y = mix(mix(left.y, vCtrl.y, t), mix(vCtrl.y, right.y, t), t); + vec2 d_half = mix(p1, v, vec2(t)); + float dy = d_half.y / d_half.x; + float width = extent.y - extent.x; + dy = abs(dy * width); + vec4 sides = vec4((dy * 0.5) + y, (dy * (-0.5)) + y, (0.5 - y) / dy, ((-0.5) - y) / dy); + sides = clamp(sides + vec4(0.5), vec4(0.0), vec4(1.0)); + float area = 0.5 * ((((sides.z - (sides.z * sides.y)) + 1.0) - sides.x) + (sides.x * sides.w)); + area *= width; + if (width == 0.0) + { + area = 0.0; + } + fragCover.x = area; +} + diff --git a/vendor/gioui.org/shader/gio/zstencil.frag.0.metallibios b/vendor/gioui.org/shader/gio/zstencil.frag.0.metallibios Binary files differnew file mode 100644 index 0000000..52f668f --- /dev/null +++ b/vendor/gioui.org/shader/gio/zstencil.frag.0.metallibios diff --git a/vendor/gioui.org/shader/gio/zstencil.frag.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zstencil.frag.0.metallibiossimulator Binary files differnew file mode 100644 index 0000000..3fbfa8f --- /dev/null +++ b/vendor/gioui.org/shader/gio/zstencil.frag.0.metallibiossimulator diff --git a/vendor/gioui.org/shader/gio/zstencil.frag.0.metallibmacos b/vendor/gioui.org/shader/gio/zstencil.frag.0.metallibmacos Binary files differnew file mode 100644 index 0000000..358a02a --- /dev/null +++ b/vendor/gioui.org/shader/gio/zstencil.frag.0.metallibmacos diff --git a/vendor/gioui.org/shader/gio/zstencil.frag.0.spirv b/vendor/gioui.org/shader/gio/zstencil.frag.0.spirv Binary files differnew file mode 100644 index 0000000..9a22d9e --- /dev/null +++ b/vendor/gioui.org/shader/gio/zstencil.frag.0.spirv diff --git a/vendor/gioui.org/shader/gio/zstencil.vert.0.dxbc b/vendor/gioui.org/shader/gio/zstencil.vert.0.dxbc Binary files differnew file mode 100644 index 0000000..505672f --- /dev/null +++ b/vendor/gioui.org/shader/gio/zstencil.vert.0.dxbc diff --git a/vendor/gioui.org/shader/gio/zstencil.vert.0.glsl100es b/vendor/gioui.org/shader/gio/zstencil.vert.0.glsl100es new file mode 100644 index 0000000..c8e1261 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zstencil.vert.0.glsl100es @@ -0,0 +1,64 @@ +#version 100 + +struct m3x2 +{ + vec3 r0; + vec3 r1; +}; + +struct Block +{ + vec4 transform; + vec2 pathOffset; +}; + +uniform Block _block; + +attribute vec2 from; +attribute vec2 ctrl; +attribute vec2 to; +attribute float maxy; +attribute float corner; +varying vec2 vFrom; +varying vec2 vCtrl; +varying vec2 vTo; + +vec3 transform3x2(m3x2 t, vec3 v) +{ + return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v)); +} + +void main() +{ + vec2 from_1 = from + _block.pathOffset; + vec2 ctrl_1 = ctrl + _block.pathOffset; + vec2 to_1 = to + _block.pathOffset; + float maxy_1 = maxy + _block.pathOffset.y; + float c = corner; + vec2 pos; + if (c >= 0.375) + { + c -= 0.5; + pos.y = maxy_1 + 1.0; + } + else + { + pos.y = min(min(from_1.y, ctrl_1.y), to_1.y) - 1.0; + } + if (c >= 0.125) + { + pos.x = max(max(from_1.x, ctrl_1.x), to_1.x) + 1.0; + } + else + { + pos.x = min(min(from_1.x, ctrl_1.x), to_1.x) - 1.0; + } + vFrom = from_1 - pos; + vCtrl = ctrl_1 - pos; + vTo = to_1 - pos; + pos = (pos * _block.transform.xy) + _block.transform.zw; + m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0)); + vec3 param_1 = vec3(pos, 0.0); + gl_Position = vec4(transform3x2(param, param_1), 1.0); +} + diff --git a/vendor/gioui.org/shader/gio/zstencil.vert.0.glsl150 b/vendor/gioui.org/shader/gio/zstencil.vert.0.glsl150 new file mode 100644 index 0000000..9ae6c9b --- /dev/null +++ b/vendor/gioui.org/shader/gio/zstencil.vert.0.glsl150 @@ -0,0 +1,64 @@ +#version 150 + +struct m3x2 +{ + vec3 r0; + vec3 r1; +}; + +struct Block +{ + vec4 transform; + vec2 pathOffset; +}; + +uniform Block _block; + +in vec2 from; +in vec2 ctrl; +in vec2 to; +in float maxy; +in float corner; +out vec2 vFrom; +out vec2 vCtrl; +out vec2 vTo; + +vec3 transform3x2(m3x2 t, vec3 v) +{ + return vec3(dot(t.r0, v), dot(t.r1, v), dot(vec3(0.0, 0.0, 1.0), v)); +} + +void main() +{ + vec2 from_1 = from + _block.pathOffset; + vec2 ctrl_1 = ctrl + _block.pathOffset; + vec2 to_1 = to + _block.pathOffset; + float maxy_1 = maxy + _block.pathOffset.y; + float c = corner; + vec2 pos; + if (c >= 0.375) + { + c -= 0.5; + pos.y = maxy_1 + 1.0; + } + else + { + pos.y = min(min(from_1.y, ctrl_1.y), to_1.y) - 1.0; + } + if (c >= 0.125) + { + pos.x = max(max(from_1.x, ctrl_1.x), to_1.x) + 1.0; + } + else + { + pos.x = min(min(from_1.x, ctrl_1.x), to_1.x) - 1.0; + } + vFrom = from_1 - pos; + vCtrl = ctrl_1 - pos; + vTo = to_1 - pos; + pos = (pos * _block.transform.xy) + _block.transform.zw; + m3x2 param = m3x2(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0)); + vec3 param_1 = vec3(pos, 0.0); + gl_Position = vec4(transform3x2(param, param_1), 1.0); +} + diff --git a/vendor/gioui.org/shader/gio/zstencil.vert.0.metallibios b/vendor/gioui.org/shader/gio/zstencil.vert.0.metallibios Binary files differnew file mode 100644 index 0000000..75ac068 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zstencil.vert.0.metallibios diff --git a/vendor/gioui.org/shader/gio/zstencil.vert.0.metallibiossimulator b/vendor/gioui.org/shader/gio/zstencil.vert.0.metallibiossimulator Binary files differnew file mode 100644 index 0000000..9ba5f58 --- /dev/null +++ b/vendor/gioui.org/shader/gio/zstencil.vert.0.metallibiossimulator diff --git a/vendor/gioui.org/shader/gio/zstencil.vert.0.metallibmacos b/vendor/gioui.org/shader/gio/zstencil.vert.0.metallibmacos Binary files differnew file mode 100644 index 0000000..4de454d --- /dev/null +++ b/vendor/gioui.org/shader/gio/zstencil.vert.0.metallibmacos diff --git a/vendor/gioui.org/shader/gio/zstencil.vert.0.spirv b/vendor/gioui.org/shader/gio/zstencil.vert.0.spirv Binary files differnew file mode 100644 index 0000000..b45803f --- /dev/null +++ b/vendor/gioui.org/shader/gio/zstencil.vert.0.spirv diff --git a/vendor/gioui.org/shader/go.mod b/vendor/gioui.org/shader/go.mod new file mode 100644 index 0000000..4c9167c --- /dev/null +++ b/vendor/gioui.org/shader/go.mod @@ -0,0 +1,5 @@ +module gioui.org/shader + +go 1.16 + +require gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334 diff --git a/vendor/gioui.org/shader/go.sum b/vendor/gioui.org/shader/go.sum new file mode 100644 index 0000000..ec5a47a --- /dev/null +++ b/vendor/gioui.org/shader/go.sum @@ -0,0 +1,2 @@ +gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334 h1:1xK224B5DnjlPKCfVDTl7+olrzgAXn4ym6dum3l34rs= +gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ= diff --git a/vendor/gioui.org/shader/piet/abi.h b/vendor/gioui.org/shader/piet/abi.h new file mode 100644 index 0000000..365d936 --- /dev/null +++ b/vendor/gioui.org/shader/piet/abi.h @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +#define ALIGN(bytes, type) type __attribute__((aligned(bytes))) + +typedef ALIGN(8, uint8_t) byte8[8]; +typedef ALIGN(8, uint16_t) word4[4]; +typedef ALIGN(4, uint32_t) dword; +typedef ALIGN(16, uint32_t) dword4[4]; +typedef ALIGN(8, uint64_t) qword; +typedef ALIGN(16, uint64_t) qword2[2]; +typedef ALIGN(16, unsigned int) uint4[4]; +typedef ALIGN(8, uint32_t) dword2[2]; +typedef ALIGN(8, unsigned short) ushort4[4]; +typedef ALIGN(16, float) float4[4]; +typedef ALIGN(16, int) int4[4]; + +typedef unsigned short half; + +typedef unsigned char bool; + +enum { + MAX_BOUND_DESCRIPTOR_SETS = 4, + MAX_DESCRIPTOR_SET_UNIFORM_BUFFERS_DYNAMIC = 8, + MAX_DESCRIPTOR_SET_STORAGE_BUFFERS_DYNAMIC = 4, + MAX_DESCRIPTOR_SET_COMBINED_BUFFERS_DYNAMIC = + MAX_DESCRIPTOR_SET_UNIFORM_BUFFERS_DYNAMIC + + MAX_DESCRIPTOR_SET_STORAGE_BUFFERS_DYNAMIC, + MAX_PUSH_CONSTANT_SIZE = 128, + + MIN_STORAGE_BUFFER_OFFSET_ALIGNMENT = 256, + + REQUIRED_MEMORY_ALIGNMENT = 16, + + SIMD_WIDTH = 4, +}; + +struct image_descriptor { + ALIGN(16, void *ptr); + int width; + int height; + int depth; + int row_pitch_bytes; + int slice_pitch_bytes; + int sample_pitch_bytes; + int sample_count; + int size_in_bytes; + + void *stencil_ptr; + int stencil_row_pitch_bytes; + int stencil_slice_pitch_bytes; + int stencil_sample_pitch_bytes; + + // TODO: unused? + void *memoryOwner; +}; + +struct buffer_descriptor { + ALIGN(16, void *ptr); + int size_in_bytes; + int robustness_size; +}; + +struct program_data { + uint8_t *descriptor_sets[MAX_BOUND_DESCRIPTOR_SETS]; + uint32_t descriptor_dynamic_offsets[MAX_DESCRIPTOR_SET_COMBINED_BUFFERS_DYNAMIC]; + uint4 num_workgroups; + uint4 workgroup_size; + uint32_t invocations_per_subgroup; + uint32_t subgroups_per_workgroup; + uint32_t invocations_per_workgroup; + unsigned char push_constants[MAX_PUSH_CONSTANT_SIZE]; + // Unused. + void *constants; +}; + +typedef int32_t yield_result; + +typedef void * coroutine; + +typedef coroutine (*routine_begin)(struct program_data *data, + int32_t workgroupX, + int32_t workgroupY, + int32_t workgroupZ, + void *workgroupMemory, + int32_t firstSubgroup, + int32_t subgroupCount); + +typedef bool (*routine_await)(coroutine r, yield_result *res); + +typedef void (*routine_destroy)(coroutine r); + diff --git a/vendor/gioui.org/shader/piet/annotated.h b/vendor/gioui.org/shader/piet/annotated.h new file mode 100644 index 0000000..6b18155 --- /dev/null +++ b/vendor/gioui.org/shader/piet/annotated.h @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense + +// Code auto-generated by piet-gpu-derive + +struct AnnoImageRef { + uint offset; +}; + +struct AnnoColorRef { + uint offset; +}; + +struct AnnoBeginClipRef { + uint offset; +}; + +struct AnnoEndClipRef { + uint offset; +}; + +struct AnnotatedRef { + uint offset; +}; + +struct AnnoImage { + vec4 bbox; + float linewidth; + uint index; + ivec2 offset; +}; + +#define AnnoImage_size 28 + +AnnoImageRef AnnoImage_index(AnnoImageRef ref, uint index) { + return AnnoImageRef(ref.offset + index * AnnoImage_size); +} + +struct AnnoColor { + vec4 bbox; + float linewidth; + uint rgba_color; +}; + +#define AnnoColor_size 24 + +AnnoColorRef AnnoColor_index(AnnoColorRef ref, uint index) { + return AnnoColorRef(ref.offset + index * AnnoColor_size); +} + +struct AnnoBeginClip { + vec4 bbox; + float linewidth; +}; + +#define AnnoBeginClip_size 20 + +AnnoBeginClipRef AnnoBeginClip_index(AnnoBeginClipRef ref, uint index) { + return AnnoBeginClipRef(ref.offset + index * AnnoBeginClip_size); +} + +struct AnnoEndClip { + vec4 bbox; +}; + +#define AnnoEndClip_size 16 + +AnnoEndClipRef AnnoEndClip_index(AnnoEndClipRef ref, uint index) { + return AnnoEndClipRef(ref.offset + index * AnnoEndClip_size); +} + +#define Annotated_Nop 0 +#define Annotated_Color 1 +#define Annotated_Image 2 +#define Annotated_BeginClip 3 +#define Annotated_EndClip 4 +#define Annotated_size 32 + +AnnotatedRef Annotated_index(AnnotatedRef ref, uint index) { + return AnnotatedRef(ref.offset + index * Annotated_size); +} + +struct AnnotatedTag { + uint tag; + uint flags; +}; + +AnnoImage AnnoImage_read(Alloc a, AnnoImageRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = read_mem(a, ix + 0); + uint raw1 = read_mem(a, ix + 1); + uint raw2 = read_mem(a, ix + 2); + uint raw3 = read_mem(a, ix + 3); + uint raw4 = read_mem(a, ix + 4); + uint raw5 = read_mem(a, ix + 5); + uint raw6 = read_mem(a, ix + 6); + AnnoImage s; + s.bbox = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.linewidth = uintBitsToFloat(raw4); + s.index = raw5; + s.offset = ivec2(int(raw6 << 16) >> 16, int(raw6) >> 16); + return s; +} + +void AnnoImage_write(Alloc a, AnnoImageRef ref, AnnoImage s) { + uint ix = ref.offset >> 2; + write_mem(a, ix + 0, floatBitsToUint(s.bbox.x)); + write_mem(a, ix + 1, floatBitsToUint(s.bbox.y)); + write_mem(a, ix + 2, floatBitsToUint(s.bbox.z)); + write_mem(a, ix + 3, floatBitsToUint(s.bbox.w)); + write_mem(a, ix + 4, floatBitsToUint(s.linewidth)); + write_mem(a, ix + 5, s.index); + write_mem(a, ix + 6, (uint(s.offset.x) & 0xffff) | (uint(s.offset.y) << 16)); +} + +AnnoColor AnnoColor_read(Alloc a, AnnoColorRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = read_mem(a, ix + 0); + uint raw1 = read_mem(a, ix + 1); + uint raw2 = read_mem(a, ix + 2); + uint raw3 = read_mem(a, ix + 3); + uint raw4 = read_mem(a, ix + 4); + uint raw5 = read_mem(a, ix + 5); + AnnoColor s; + s.bbox = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.linewidth = uintBitsToFloat(raw4); + s.rgba_color = raw5; + return s; +} + +void AnnoColor_write(Alloc a, AnnoColorRef ref, AnnoColor s) { + uint ix = ref.offset >> 2; + write_mem(a, ix + 0, floatBitsToUint(s.bbox.x)); + write_mem(a, ix + 1, floatBitsToUint(s.bbox.y)); + write_mem(a, ix + 2, floatBitsToUint(s.bbox.z)); + write_mem(a, ix + 3, floatBitsToUint(s.bbox.w)); + write_mem(a, ix + 4, floatBitsToUint(s.linewidth)); + write_mem(a, ix + 5, s.rgba_color); +} + +AnnoBeginClip AnnoBeginClip_read(Alloc a, AnnoBeginClipRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = read_mem(a, ix + 0); + uint raw1 = read_mem(a, ix + 1); + uint raw2 = read_mem(a, ix + 2); + uint raw3 = read_mem(a, ix + 3); + uint raw4 = read_mem(a, ix + 4); + AnnoBeginClip s; + s.bbox = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.linewidth = uintBitsToFloat(raw4); + return s; +} + +void AnnoBeginClip_write(Alloc a, AnnoBeginClipRef ref, AnnoBeginClip s) { + uint ix = ref.offset >> 2; + write_mem(a, ix + 0, floatBitsToUint(s.bbox.x)); + write_mem(a, ix + 1, floatBitsToUint(s.bbox.y)); + write_mem(a, ix + 2, floatBitsToUint(s.bbox.z)); + write_mem(a, ix + 3, floatBitsToUint(s.bbox.w)); + write_mem(a, ix + 4, floatBitsToUint(s.linewidth)); +} + +AnnoEndClip AnnoEndClip_read(Alloc a, AnnoEndClipRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = read_mem(a, ix + 0); + uint raw1 = read_mem(a, ix + 1); + uint raw2 = read_mem(a, ix + 2); + uint raw3 = read_mem(a, ix + 3); + AnnoEndClip s; + s.bbox = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + return s; +} + +void AnnoEndClip_write(Alloc a, AnnoEndClipRef ref, AnnoEndClip s) { + uint ix = ref.offset >> 2; + write_mem(a, ix + 0, floatBitsToUint(s.bbox.x)); + write_mem(a, ix + 1, floatBitsToUint(s.bbox.y)); + write_mem(a, ix + 2, floatBitsToUint(s.bbox.z)); + write_mem(a, ix + 3, floatBitsToUint(s.bbox.w)); +} + +AnnotatedTag Annotated_tag(Alloc a, AnnotatedRef ref) { + uint tag_and_flags = read_mem(a, ref.offset >> 2); + return AnnotatedTag(tag_and_flags & 0xffff, tag_and_flags >> 16); +} + +AnnoColor Annotated_Color_read(Alloc a, AnnotatedRef ref) { + return AnnoColor_read(a, AnnoColorRef(ref.offset + 4)); +} + +AnnoImage Annotated_Image_read(Alloc a, AnnotatedRef ref) { + return AnnoImage_read(a, AnnoImageRef(ref.offset + 4)); +} + +AnnoBeginClip Annotated_BeginClip_read(Alloc a, AnnotatedRef ref) { + return AnnoBeginClip_read(a, AnnoBeginClipRef(ref.offset + 4)); +} + +AnnoEndClip Annotated_EndClip_read(Alloc a, AnnotatedRef ref) { + return AnnoEndClip_read(a, AnnoEndClipRef(ref.offset + 4)); +} + +void Annotated_Nop_write(Alloc a, AnnotatedRef ref) { + write_mem(a, ref.offset >> 2, Annotated_Nop); +} + +void Annotated_Color_write(Alloc a, AnnotatedRef ref, uint flags, AnnoColor s) { + write_mem(a, ref.offset >> 2, (flags << 16) | Annotated_Color); + AnnoColor_write(a, AnnoColorRef(ref.offset + 4), s); +} + +void Annotated_Image_write(Alloc a, AnnotatedRef ref, uint flags, AnnoImage s) { + write_mem(a, ref.offset >> 2, (flags << 16) | Annotated_Image); + AnnoImage_write(a, AnnoImageRef(ref.offset + 4), s); +} + +void Annotated_BeginClip_write(Alloc a, AnnotatedRef ref, uint flags, AnnoBeginClip s) { + write_mem(a, ref.offset >> 2, (flags << 16) | Annotated_BeginClip); + AnnoBeginClip_write(a, AnnoBeginClipRef(ref.offset + 4), s); +} + +void Annotated_EndClip_write(Alloc a, AnnotatedRef ref, AnnoEndClip s) { + write_mem(a, ref.offset >> 2, Annotated_EndClip); + AnnoEndClip_write(a, AnnoEndClipRef(ref.offset + 4), s); +} + diff --git a/vendor/gioui.org/shader/piet/backdrop.comp b/vendor/gioui.org/shader/piet/backdrop.comp new file mode 100644 index 0000000..12ae5b1 --- /dev/null +++ b/vendor/gioui.org/shader/piet/backdrop.comp @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense + +// Propagation of tile backdrop for filling. +// +// Each thread reads one path element and calculates the number of spanned tiles +// based on the bounding box. +// In a further compaction step, the workgroup loops over the corresponding tile rows per element in parallel. +// For each row the per tile backdrop will be read, as calculated in the previous coarse path segment kernel, +// and propagated from the left to the right (prefix summed). +// +// Output state: +// - Each path element has an array of tiles covering the whole path based on boundig box +// - Each tile per path element contains the 'backdrop' and a list of subdivided path segments + +#version 450 +#extension GL_GOOGLE_include_directive : enable + +#include "mem.h" +#include "setup.h" + +#define LG_BACKDROP_WG (7 + LG_WG_FACTOR) +#define BACKDROP_WG (1 << LG_BACKDROP_WG) + +layout(local_size_x = BACKDROP_WG, local_size_y = 1) in; + +layout(set = 0, binding = 1) readonly buffer ConfigBuf { + Config conf; +}; + +#include "annotated.h" +#include "tile.h" + +shared uint sh_row_count[BACKDROP_WG]; +shared Alloc sh_row_alloc[BACKDROP_WG]; +shared uint sh_row_width[BACKDROP_WG]; + +void main() { + uint th_ix = gl_LocalInvocationID.x; + uint element_ix = gl_GlobalInvocationID.x; + AnnotatedRef ref = AnnotatedRef(conf.anno_alloc.offset + element_ix * Annotated_size); + + // Work assignment: 1 thread : 1 path element + uint row_count = 0; + bool mem_ok = mem_error == NO_ERROR; + if (element_ix < conf.n_elements) { + AnnotatedTag tag = Annotated_tag(conf.anno_alloc, ref); + switch (tag.tag) { + case Annotated_Image: + case Annotated_BeginClip: + case Annotated_Color: + if (fill_mode_from_flags(tag.flags) != MODE_NONZERO) { + break; + } + // Fall through. + PathRef path_ref = PathRef(conf.tile_alloc.offset + element_ix * Path_size); + Path path = Path_read(conf.tile_alloc, path_ref); + sh_row_width[th_ix] = path.bbox.z - path.bbox.x; + row_count = path.bbox.w - path.bbox.y; + // Paths that don't cross tile top edges don't have backdrops. + // Don't apply the optimization to paths that may cross the y = 0 + // top edge, but clipped to 1 row. + if (row_count == 1 && path.bbox.y > 0) { + // Note: this can probably be expanded to width = 2 as + // long as it doesn't cross the left edge. + row_count = 0; + } + Alloc path_alloc = new_alloc(path.tiles.offset, (path.bbox.z - path.bbox.x) * (path.bbox.w - path.bbox.y) * Tile_size, mem_ok); + sh_row_alloc[th_ix] = path_alloc; + } + } + + sh_row_count[th_ix] = row_count; + // Prefix sum of sh_row_count + for (uint i = 0; i < LG_BACKDROP_WG; i++) { + barrier(); + if (th_ix >= (1 << i)) { + row_count += sh_row_count[th_ix - (1 << i)]; + } + barrier(); + sh_row_count[th_ix] = row_count; + } + barrier(); + // Work assignment: 1 thread : 1 path element row + uint total_rows = sh_row_count[BACKDROP_WG - 1]; + for (uint row = th_ix; row < total_rows; row += BACKDROP_WG) { + // Binary search to find element + uint el_ix = 0; + for (uint i = 0; i < LG_BACKDROP_WG; i++) { + uint probe = el_ix + ((BACKDROP_WG / 2) >> i); + if (row >= sh_row_count[probe - 1]) { + el_ix = probe; + } + } + uint width = sh_row_width[el_ix]; + if (width > 0 && mem_ok) { + // Process one row sequentially + // Read backdrop value per tile and prefix sum it + Alloc tiles_alloc = sh_row_alloc[el_ix]; + uint seq_ix = row - (el_ix > 0 ? sh_row_count[el_ix - 1] : 0); + uint tile_el_ix = (tiles_alloc.offset >> 2) + 1 + seq_ix * 2 * width; + uint sum = read_mem(tiles_alloc, tile_el_ix); + for (uint x = 1; x < width; x++) { + tile_el_ix += 2; + sum += read_mem(tiles_alloc, tile_el_ix); + write_mem(tiles_alloc, tile_el_ix, sum); + } + } + } +} diff --git a/vendor/gioui.org/shader/piet/backdrop_abi.c b/vendor/gioui.org/shader/piet/backdrop_abi.c new file mode 100644 index 0000000..48a4a30 --- /dev/null +++ b/vendor/gioui.org/shader/piet/backdrop_abi.c @@ -0,0 +1,23 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +//go:build linux && (arm64 || arm || amd64) +// +build linux +// +build arm64 arm amd64 + +#include <stdint.h> +#include <stddef.h> +#include "abi.h" +#include "runtime.h" +#include "backdrop_abi.h" + +const struct program_info backdrop_program_info = { + .has_cbarriers = 1, + .min_memory_size = 100000, + .desc_set_size = sizeof(struct backdrop_descriptor_set_layout), + .workgroup_size_x = 128, + .workgroup_size_y = 1, + .workgroup_size_z = 1, + .begin = backdrop_coroutine_begin, + .await = backdrop_coroutine_await, + .destroy = backdrop_coroutine_destroy, +}; diff --git a/vendor/gioui.org/shader/piet/backdrop_abi.go b/vendor/gioui.org/shader/piet/backdrop_abi.go new file mode 100644 index 0000000..a40a37d --- /dev/null +++ b/vendor/gioui.org/shader/piet/backdrop_abi.go @@ -0,0 +1,35 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +//go:build linux && (arm64 || arm || amd64) +// +build linux +// +build arm64 arm amd64 + +package piet + +import "gioui.org/cpu" +import "unsafe" + +/* +#cgo LDFLAGS: -lm + +#include <stdint.h> +#include <stdlib.h> +#include "abi.h" +#include "runtime.h" +#include "backdrop_abi.h" +*/ +import "C" + +var BackdropProgramInfo = (*cpu.ProgramInfo)(unsafe.Pointer(&C.backdrop_program_info)) + +type BackdropDescriptorSetLayout = C.struct_backdrop_descriptor_set_layout + +const BackdropHash = "6862eaf623d89da635e9d5bc981c77aae4e39aa44047ab47c62d243cf5fe7e73" + +func (l *BackdropDescriptorSetLayout) Binding0() *cpu.BufferDescriptor { + return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding0)) +} + +func (l *BackdropDescriptorSetLayout) Binding1() *cpu.BufferDescriptor { + return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding1)) +} diff --git a/vendor/gioui.org/shader/piet/backdrop_abi.h b/vendor/gioui.org/shader/piet/backdrop_abi.h new file mode 100644 index 0000000..f5c0303 --- /dev/null +++ b/vendor/gioui.org/shader/piet/backdrop_abi.h @@ -0,0 +1,17 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +struct backdrop_descriptor_set_layout { + struct buffer_descriptor binding0; + struct buffer_descriptor binding1; +}; + +extern coroutine backdrop_coroutine_begin(struct program_data *data, + int32_t workgroupX, int32_t workgroupY, int32_t workgroupZ, + void *workgroupMemory, + int32_t firstSubgroup, + int32_t subgroupCount) ATTR_HIDDEN; + +extern bool backdrop_coroutine_await(coroutine r, yield_result *res) ATTR_HIDDEN; +extern void backdrop_coroutine_destroy(coroutine r) ATTR_HIDDEN; + +extern const struct program_info backdrop_program_info ATTR_HIDDEN; diff --git a/vendor/gioui.org/shader/piet/backdrop_abi_nosupport.go b/vendor/gioui.org/shader/piet/backdrop_abi_nosupport.go new file mode 100644 index 0000000..c60119b --- /dev/null +++ b/vendor/gioui.org/shader/piet/backdrop_abi_nosupport.go @@ -0,0 +1,22 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +//go:build !(linux && (arm64 || arm || amd64)) +// +build !linux !arm64,!arm,!amd64 + +package piet + +import "gioui.org/cpu" + +var BackdropProgramInfo *cpu.ProgramInfo + +type BackdropDescriptorSetLayout struct{} + +const BackdropHash = "" + +func (l *BackdropDescriptorSetLayout) Binding0() *cpu.BufferDescriptor { + panic("unsupported") +} + +func (l *BackdropDescriptorSetLayout) Binding1() *cpu.BufferDescriptor { + panic("unsupported") +} diff --git a/vendor/gioui.org/shader/piet/backdrop_linux_amd64.syso b/vendor/gioui.org/shader/piet/backdrop_linux_amd64.syso Binary files differnew file mode 100644 index 0000000..2a71e8a --- /dev/null +++ b/vendor/gioui.org/shader/piet/backdrop_linux_amd64.syso diff --git a/vendor/gioui.org/shader/piet/backdrop_linux_arm.syso b/vendor/gioui.org/shader/piet/backdrop_linux_arm.syso Binary files differnew file mode 100644 index 0000000..c86dada --- /dev/null +++ b/vendor/gioui.org/shader/piet/backdrop_linux_arm.syso diff --git a/vendor/gioui.org/shader/piet/backdrop_linux_arm64.syso b/vendor/gioui.org/shader/piet/backdrop_linux_arm64.syso Binary files differnew file mode 100644 index 0000000..4608a7e --- /dev/null +++ b/vendor/gioui.org/shader/piet/backdrop_linux_arm64.syso diff --git a/vendor/gioui.org/shader/piet/binning.comp b/vendor/gioui.org/shader/piet/binning.comp new file mode 100644 index 0000000..acda83c --- /dev/null +++ b/vendor/gioui.org/shader/piet/binning.comp @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense + +// The binning stage of the pipeline. +// +// Each workgroup processes N_TILE paths. +// Each thread processes one path and calculates a N_TILE_X x N_TILE_Y coverage mask +// based on the path bounding box to bin the paths. + +#version 450 +#extension GL_GOOGLE_include_directive : enable + +#include "mem.h" +#include "setup.h" + +layout(local_size_x = N_TILE, local_size_y = 1) in; + +layout(set = 0, binding = 1) readonly buffer ConfigBuf { + Config conf; +}; + +#include "annotated.h" +#include "bins.h" + +// scale factors useful for converting coordinates to bins +#define SX (1.0 / float(N_TILE_X * TILE_WIDTH_PX)) +#define SY (1.0 / float(N_TILE_Y * TILE_HEIGHT_PX)) + +// Constant not available in GLSL. Also consider uintBitsToFloat(0x7f800000) +#define INFINITY (1.0 / 0.0) + +// Note: cudaraster has N_TILE + 1 to cut down on bank conflicts. +// Bitmaps are sliced (256bit into 8 (N_SLICE) 32bit submaps) +shared uint bitmaps[N_SLICE][N_TILE]; +shared uint count[N_SLICE][N_TILE]; +shared Alloc sh_chunk_alloc[N_TILE]; +// Really a bool, but some Metal devices don't accept shared bools. +shared uint sh_alloc_failed; + +void main() { + uint my_n_elements = conf.n_elements; + uint my_partition = gl_WorkGroupID.x; + + for (uint i = 0; i < N_SLICE; i++) { + bitmaps[i][gl_LocalInvocationID.x] = 0; + } + if (gl_LocalInvocationID.x == 0) { + sh_alloc_failed = 0; + } + barrier(); + + // Read inputs and determine coverage of bins + uint element_ix = my_partition * N_TILE + gl_LocalInvocationID.x; + AnnotatedRef ref = AnnotatedRef(conf.anno_alloc.offset + element_ix * Annotated_size); + uint tag = Annotated_Nop; + if (element_ix < my_n_elements) { + tag = Annotated_tag(conf.anno_alloc, ref).tag; + } + int x0 = 0, y0 = 0, x1 = 0, y1 = 0; + switch (tag) { + case Annotated_Color: + case Annotated_Image: + case Annotated_BeginClip: + case Annotated_EndClip: + // Note: we take advantage of the fact that these drawing elements + // have the bbox at the same place in their layout. + AnnoEndClip clip = Annotated_EndClip_read(conf.anno_alloc, ref); + x0 = int(floor(clip.bbox.x * SX)); + y0 = int(floor(clip.bbox.y * SY)); + x1 = int(ceil(clip.bbox.z * SX)); + y1 = int(ceil(clip.bbox.w * SY)); + break; + } + + // At this point, we run an iterator over the coverage area, + // trying to keep divergence low. + // Right now, it's just a bbox, but we'll get finer with + // segments. + uint width_in_bins = (conf.width_in_tiles + N_TILE_X - 1)/N_TILE_X; + uint height_in_bins = (conf.height_in_tiles + N_TILE_Y - 1)/N_TILE_Y; + x0 = clamp(x0, 0, int(width_in_bins)); + x1 = clamp(x1, x0, int(width_in_bins)); + y0 = clamp(y0, 0, int(height_in_bins)); + y1 = clamp(y1, y0, int(height_in_bins)); + if (x0 == x1) y1 = y0; + int x = x0, y = y0; + uint my_slice = gl_LocalInvocationID.x / 32; + uint my_mask = 1 << (gl_LocalInvocationID.x & 31); + while (y < y1) { + atomicOr(bitmaps[my_slice][y * width_in_bins + x], my_mask); + x++; + if (x == x1) { + x = x0; + y++; + } + } + + barrier(); + // Allocate output segments. + uint element_count = 0; + for (uint i = 0; i < N_SLICE; i++) { + element_count += bitCount(bitmaps[i][gl_LocalInvocationID.x]); + count[i][gl_LocalInvocationID.x] = element_count; + } + // element_count is number of elements covering bin for this invocation. + Alloc chunk_alloc = new_alloc(0, 0, true); + if (element_count != 0) { + // TODO: aggregate atomic adds (subgroup is probably fastest) + MallocResult chunk = malloc(element_count * BinInstance_size); + chunk_alloc = chunk.alloc; + sh_chunk_alloc[gl_LocalInvocationID.x] = chunk_alloc; + if (chunk.failed) { + sh_alloc_failed = 1; + } + } + // Note: it might be more efficient for reading to do this in the + // other order (each bin is a contiguous sequence of partitions) + uint out_ix = (conf.bin_alloc.offset >> 2) + (my_partition * N_TILE + gl_LocalInvocationID.x) * 2; + write_mem(conf.bin_alloc, out_ix, element_count); + write_mem(conf.bin_alloc, out_ix + 1, chunk_alloc.offset); + + barrier(); + if (sh_alloc_failed != 0 || mem_error != NO_ERROR) { + return; + } + + // Use similar strategy as Laine & Karras paper; loop over bbox of bins + // touched by this element + x = x0; + y = y0; + while (y < y1) { + uint bin_ix = y * width_in_bins + x; + uint out_mask = bitmaps[my_slice][bin_ix]; + if ((out_mask & my_mask) != 0) { + uint idx = bitCount(out_mask & (my_mask - 1)); + if (my_slice > 0) { + idx += count[my_slice - 1][bin_ix]; + } + Alloc out_alloc = sh_chunk_alloc[bin_ix]; + uint out_offset = out_alloc.offset + idx * BinInstance_size; + BinInstance_write(out_alloc, BinInstanceRef(out_offset), BinInstance(element_ix)); + } + x++; + if (x == x1) { + x = x0; + y++; + } + } +} diff --git a/vendor/gioui.org/shader/piet/binning_abi.c b/vendor/gioui.org/shader/piet/binning_abi.c new file mode 100644 index 0000000..b28be33 --- /dev/null +++ b/vendor/gioui.org/shader/piet/binning_abi.c @@ -0,0 +1,23 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +//go:build linux && (arm64 || arm || amd64) +// +build linux +// +build arm64 arm amd64 + +#include <stdint.h> +#include <stddef.h> +#include "abi.h" +#include "runtime.h" +#include "binning_abi.h" + +const struct program_info binning_program_info = { + .has_cbarriers = 1, + .min_memory_size = 100000, + .desc_set_size = sizeof(struct binning_descriptor_set_layout), + .workgroup_size_x = 128, + .workgroup_size_y = 1, + .workgroup_size_z = 1, + .begin = binning_coroutine_begin, + .await = binning_coroutine_await, + .destroy = binning_coroutine_destroy, +}; diff --git a/vendor/gioui.org/shader/piet/binning_abi.go b/vendor/gioui.org/shader/piet/binning_abi.go new file mode 100644 index 0000000..7ee361f --- /dev/null +++ b/vendor/gioui.org/shader/piet/binning_abi.go @@ -0,0 +1,35 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +//go:build linux && (arm64 || arm || amd64) +// +build linux +// +build arm64 arm amd64 + +package piet + +import "gioui.org/cpu" +import "unsafe" + +/* +#cgo LDFLAGS: -lm + +#include <stdint.h> +#include <stdlib.h> +#include "abi.h" +#include "runtime.h" +#include "binning_abi.h" +*/ +import "C" + +var BinningProgramInfo = (*cpu.ProgramInfo)(unsafe.Pointer(&C.binning_program_info)) + +type BinningDescriptorSetLayout = C.struct_binning_descriptor_set_layout + +const BinningHash = "84177b6dfb90309a6c054ab9fea42293bd49033c221651c756eb40188f4d6ce8" + +func (l *BinningDescriptorSetLayout) Binding0() *cpu.BufferDescriptor { + return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding0)) +} + +func (l *BinningDescriptorSetLayout) Binding1() *cpu.BufferDescriptor { + return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding1)) +} diff --git a/vendor/gioui.org/shader/piet/binning_abi.h b/vendor/gioui.org/shader/piet/binning_abi.h new file mode 100644 index 0000000..0152f34 --- /dev/null +++ b/vendor/gioui.org/shader/piet/binning_abi.h @@ -0,0 +1,17 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +struct binning_descriptor_set_layout { + struct buffer_descriptor binding0; + struct buffer_descriptor binding1; +}; + +extern coroutine binning_coroutine_begin(struct program_data *data, + int32_t workgroupX, int32_t workgroupY, int32_t workgroupZ, + void *workgroupMemory, + int32_t firstSubgroup, + int32_t subgroupCount) ATTR_HIDDEN; + +extern bool binning_coroutine_await(coroutine r, yield_result *res) ATTR_HIDDEN; +extern void binning_coroutine_destroy(coroutine r) ATTR_HIDDEN; + +extern const struct program_info binning_program_info ATTR_HIDDEN; diff --git a/vendor/gioui.org/shader/piet/binning_abi_nosupport.go b/vendor/gioui.org/shader/piet/binning_abi_nosupport.go new file mode 100644 index 0000000..81b9b56 --- /dev/null +++ b/vendor/gioui.org/shader/piet/binning_abi_nosupport.go @@ -0,0 +1,22 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +//go:build !(linux && (arm64 || arm || amd64)) +// +build !linux !arm64,!arm,!amd64 + +package piet + +import "gioui.org/cpu" + +var BinningProgramInfo *cpu.ProgramInfo + +type BinningDescriptorSetLayout struct{} + +const BinningHash = "" + +func (l *BinningDescriptorSetLayout) Binding0() *cpu.BufferDescriptor { + panic("unsupported") +} + +func (l *BinningDescriptorSetLayout) Binding1() *cpu.BufferDescriptor { + panic("unsupported") +} diff --git a/vendor/gioui.org/shader/piet/binning_linux_amd64.syso b/vendor/gioui.org/shader/piet/binning_linux_amd64.syso Binary files differnew file mode 100644 index 0000000..329d95d --- /dev/null +++ b/vendor/gioui.org/shader/piet/binning_linux_amd64.syso diff --git a/vendor/gioui.org/shader/piet/binning_linux_arm.syso b/vendor/gioui.org/shader/piet/binning_linux_arm.syso Binary files differnew file mode 100644 index 0000000..a8f21a7 --- /dev/null +++ b/vendor/gioui.org/shader/piet/binning_linux_arm.syso diff --git a/vendor/gioui.org/shader/piet/binning_linux_arm64.syso b/vendor/gioui.org/shader/piet/binning_linux_arm64.syso Binary files differnew file mode 100644 index 0000000..02938c5 --- /dev/null +++ b/vendor/gioui.org/shader/piet/binning_linux_arm64.syso diff --git a/vendor/gioui.org/shader/piet/bins.h b/vendor/gioui.org/shader/piet/bins.h new file mode 100644 index 0000000..853adab --- /dev/null +++ b/vendor/gioui.org/shader/piet/bins.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense + +// Code auto-generated by piet-gpu-derive + +struct BinInstanceRef { + uint offset; +}; + +struct BinInstance { + uint element_ix; +}; + +#define BinInstance_size 4 + +BinInstanceRef BinInstance_index(BinInstanceRef ref, uint index) { + return BinInstanceRef(ref.offset + index * BinInstance_size); +} + +BinInstance BinInstance_read(Alloc a, BinInstanceRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = read_mem(a, ix + 0); + BinInstance s; + s.element_ix = raw0; + return s; +} + +void BinInstance_write(Alloc a, BinInstanceRef ref, BinInstance s) { + uint ix = ref.offset >> 2; + write_mem(a, ix + 0, s.element_ix); +} + diff --git a/vendor/gioui.org/shader/piet/coarse.comp b/vendor/gioui.org/shader/piet/coarse.comp new file mode 100644 index 0000000..731da97 --- /dev/null +++ b/vendor/gioui.org/shader/piet/coarse.comp @@ -0,0 +1,426 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense + +// The coarse rasterizer stage of the pipeline. +// +// As input we have the ordered partitions of paths from the binning phase and +// the annotated tile list of segments and backdrop per path. +// +// Each workgroup operating on one bin by stream compacting +// the elements corresponding to the bin. +// +// As output we have an ordered command stream per tile. Every tile from a path (backdrop + segment list) will be encoded. + +#version 450 +#extension GL_GOOGLE_include_directive : enable + +#include "mem.h" +#include "setup.h" + +layout(local_size_x = N_TILE, local_size_y = 1) in; + +layout(set = 0, binding = 1) readonly buffer ConfigBuf { + Config conf; +}; + +#include "annotated.h" +#include "bins.h" +#include "tile.h" +#include "ptcl.h" + +#define LG_N_PART_READ (7 + LG_WG_FACTOR) +#define N_PART_READ (1 << LG_N_PART_READ) + +shared uint sh_elements[N_TILE]; + +// Number of elements in the partition; prefix sum. +shared uint sh_part_count[N_PART_READ]; +shared Alloc sh_part_elements[N_PART_READ]; + +shared uint sh_bitmaps[N_SLICE][N_TILE]; + +shared uint sh_tile_count[N_TILE]; +// The width of the tile rect for the element, intersected with this bin +shared uint sh_tile_width[N_TILE]; +shared uint sh_tile_x0[N_TILE]; +shared uint sh_tile_y0[N_TILE]; + +// These are set up so base + tile_y * stride + tile_x points to a Tile. +shared uint sh_tile_base[N_TILE]; +shared uint sh_tile_stride[N_TILE]; + +#ifdef MEM_DEBUG +// Store allocs only when MEM_DEBUG to save shared memory traffic. +shared Alloc sh_tile_alloc[N_TILE]; + +void write_tile_alloc(uint el_ix, Alloc a) { + sh_tile_alloc[el_ix] = a; +} + +Alloc read_tile_alloc(uint el_ix, bool mem_ok) { + return sh_tile_alloc[el_ix]; +} +#else +void write_tile_alloc(uint el_ix, Alloc a) { + // No-op +} + +Alloc read_tile_alloc(uint el_ix, bool mem_ok) { + // All memory. + return new_alloc(0, memory.length()*4, mem_ok); +} +#endif + +// The maximum number of commands per annotated element. +#define ANNO_COMMANDS 2 + +// Perhaps cmd_alloc should be a global? This is a style question. +bool alloc_cmd(inout Alloc cmd_alloc, inout CmdRef cmd_ref, inout uint cmd_limit) { + if (cmd_ref.offset < cmd_limit) { + return true; + } + MallocResult new_cmd = malloc(PTCL_INITIAL_ALLOC); + if (new_cmd.failed) { + return false; + } + CmdJump jump = CmdJump(new_cmd.alloc.offset); + Cmd_Jump_write(cmd_alloc, cmd_ref, jump); + cmd_alloc = new_cmd.alloc; + cmd_ref = CmdRef(cmd_alloc.offset); + // Reserve space for the maximum number of commands and a potential jump. + cmd_limit = cmd_alloc.offset + PTCL_INITIAL_ALLOC - (ANNO_COMMANDS + 1) * Cmd_size; + return true; +} + +void write_fill(Alloc alloc, inout CmdRef cmd_ref, uint flags, Tile tile, float linewidth) { + if (fill_mode_from_flags(flags) == MODE_NONZERO) { + if (tile.tile.offset != 0) { + CmdFill cmd_fill = CmdFill(tile.tile.offset, tile.backdrop); + Cmd_Fill_write(alloc, cmd_ref, cmd_fill); + cmd_ref.offset += 4 + CmdFill_size; + } else { + Cmd_Solid_write(alloc, cmd_ref); + cmd_ref.offset += 4; + } + } else { + CmdStroke cmd_stroke = CmdStroke(tile.tile.offset, 0.5 * linewidth); + Cmd_Stroke_write(alloc, cmd_ref, cmd_stroke); + cmd_ref.offset += 4 + CmdStroke_size; + } +} + +void main() { + // Could use either linear or 2d layouts for both dispatch and + // invocations within the workgroup. We'll use variables to abstract. + uint width_in_bins = (conf.width_in_tiles + N_TILE_X - 1)/N_TILE_X; + uint bin_ix = width_in_bins * gl_WorkGroupID.y + gl_WorkGroupID.x; + uint partition_ix = 0; + uint n_partitions = (conf.n_elements + N_TILE - 1) / N_TILE; + uint th_ix = gl_LocalInvocationID.x; + + // Coordinates of top left of bin, in tiles. + uint bin_tile_x = N_TILE_X * gl_WorkGroupID.x; + uint bin_tile_y = N_TILE_Y * gl_WorkGroupID.y; + + // Per-tile state + uint tile_x = gl_LocalInvocationID.x % N_TILE_X; + uint tile_y = gl_LocalInvocationID.x / N_TILE_X; + uint this_tile_ix = (bin_tile_y + tile_y) * conf.width_in_tiles + bin_tile_x + tile_x; + Alloc cmd_alloc = slice_mem(conf.ptcl_alloc, this_tile_ix * PTCL_INITIAL_ALLOC, PTCL_INITIAL_ALLOC); + CmdRef cmd_ref = CmdRef(cmd_alloc.offset); + // Reserve space for the maximum number of commands and a potential jump. + uint cmd_limit = cmd_ref.offset + PTCL_INITIAL_ALLOC - (ANNO_COMMANDS + 1) * Cmd_size; + // The nesting depth of the clip stack + uint clip_depth = 0; + // State for the "clip zero" optimization. If it's nonzero, then we are + // currently in a clip for which the entire tile has an alpha of zero, and + // the value is the depth after the "begin clip" of that element. + uint clip_zero_depth = 0; + // State for the "clip one" optimization. If bit `i` is set, then that means + // that the clip pushed at depth `i` has an alpha of all one. + uint clip_one_mask = 0; + + // I'm sure we can figure out how to do this with at least one fewer register... + // Items up to rd_ix have been read from sh_elements + uint rd_ix = 0; + // Items up to wr_ix have been written into sh_elements + uint wr_ix = 0; + // Items between part_start_ix and ready_ix are ready to be transferred from sh_part_elements + uint part_start_ix = 0; + uint ready_ix = 0; + + // Leave room for the fine rasterizer scratch allocation. + Alloc scratch_alloc = slice_mem(cmd_alloc, 0, Alloc_size); + cmd_ref.offset += Alloc_size; + + uint num_begin_slots = 0; + uint begin_slot = 0; + bool mem_ok = mem_error == NO_ERROR; + while (true) { + for (uint i = 0; i < N_SLICE; i++) { + sh_bitmaps[i][th_ix] = 0; + } + + // parallel read of input partitions + do { + if (ready_ix == wr_ix && partition_ix < n_partitions) { + part_start_ix = ready_ix; + uint count = 0; + if (th_ix < N_PART_READ && partition_ix + th_ix < n_partitions) { + uint in_ix = (conf.bin_alloc.offset >> 2) + ((partition_ix + th_ix) * N_TILE + bin_ix) * 2; + count = read_mem(conf.bin_alloc, in_ix); + uint offset = read_mem(conf.bin_alloc, in_ix + 1); + sh_part_elements[th_ix] = new_alloc(offset, count*BinInstance_size, mem_ok); + } + // prefix sum of counts + for (uint i = 0; i < LG_N_PART_READ; i++) { + if (th_ix < N_PART_READ) { + sh_part_count[th_ix] = count; + } + barrier(); + if (th_ix < N_PART_READ) { + if (th_ix >= (1 << i)) { + count += sh_part_count[th_ix - (1 << i)]; + } + } + barrier(); + } + if (th_ix < N_PART_READ) { + sh_part_count[th_ix] = part_start_ix + count; + } + barrier(); + ready_ix = sh_part_count[N_PART_READ - 1]; + partition_ix += N_PART_READ; + } + // use binary search to find element to read + uint ix = rd_ix + th_ix; + if (ix >= wr_ix && ix < ready_ix && mem_ok) { + uint part_ix = 0; + for (uint i = 0; i < LG_N_PART_READ; i++) { + uint probe = part_ix + ((N_PART_READ / 2) >> i); + if (ix >= sh_part_count[probe - 1]) { + part_ix = probe; + } + } + ix -= part_ix > 0 ? sh_part_count[part_ix - 1] : part_start_ix; + Alloc bin_alloc = sh_part_elements[part_ix]; + BinInstanceRef inst_ref = BinInstanceRef(bin_alloc.offset); + BinInstance inst = BinInstance_read(bin_alloc, BinInstance_index(inst_ref, ix)); + sh_elements[th_ix] = inst.element_ix; + } + barrier(); + + wr_ix = min(rd_ix + N_TILE, ready_ix); + } while (wr_ix - rd_ix < N_TILE && (wr_ix < ready_ix || partition_ix < n_partitions)); + + // We've done the merge and filled the buffer. + + // Read one element, compute coverage. + uint tag = Annotated_Nop; + uint element_ix; + AnnotatedRef ref; + if (th_ix + rd_ix < wr_ix) { + element_ix = sh_elements[th_ix]; + ref = AnnotatedRef(conf.anno_alloc.offset + element_ix * Annotated_size); + tag = Annotated_tag(conf.anno_alloc, ref).tag; + } + + // Bounding box of element in pixel coordinates. + uint tile_count; + switch (tag) { + case Annotated_Color: + case Annotated_Image: + case Annotated_BeginClip: + case Annotated_EndClip: + // We have one "path" for each element, even if the element isn't + // actually a path (currently EndClip, but images etc in the future). + uint path_ix = element_ix; + Path path = Path_read(conf.tile_alloc, PathRef(conf.tile_alloc.offset + path_ix * Path_size)); + uint stride = path.bbox.z - path.bbox.x; + sh_tile_stride[th_ix] = stride; + int dx = int(path.bbox.x) - int(bin_tile_x); + int dy = int(path.bbox.y) - int(bin_tile_y); + int x0 = clamp(dx, 0, N_TILE_X); + int y0 = clamp(dy, 0, N_TILE_Y); + int x1 = clamp(int(path.bbox.z) - int(bin_tile_x), 0, N_TILE_X); + int y1 = clamp(int(path.bbox.w) - int(bin_tile_y), 0, N_TILE_Y); + sh_tile_width[th_ix] = uint(x1 - x0); + sh_tile_x0[th_ix] = x0; + sh_tile_y0[th_ix] = y0; + tile_count = uint(x1 - x0) * uint(y1 - y0); + // base relative to bin + uint base = path.tiles.offset - uint(dy * stride + dx) * Tile_size; + sh_tile_base[th_ix] = base; + Alloc path_alloc = new_alloc(path.tiles.offset, (path.bbox.z - path.bbox.x) * (path.bbox.w - path.bbox.y) * Tile_size, mem_ok); + write_tile_alloc(th_ix, path_alloc); + break; + default: + tile_count = 0; + break; + } + + // Prefix sum of sh_tile_count + sh_tile_count[th_ix] = tile_count; + for (uint i = 0; i < LG_N_TILE; i++) { + barrier(); + if (th_ix >= (1 << i)) { + tile_count += sh_tile_count[th_ix - (1 << i)]; + } + barrier(); + sh_tile_count[th_ix] = tile_count; + } + barrier(); + uint total_tile_count = sh_tile_count[N_TILE - 1]; + for (uint ix = th_ix; ix < total_tile_count; ix += N_TILE) { + // Binary search to find element + uint el_ix = 0; + for (uint i = 0; i < LG_N_TILE; i++) { + uint probe = el_ix + ((N_TILE / 2) >> i); + if (ix >= sh_tile_count[probe - 1]) { + el_ix = probe; + } + } + AnnotatedRef ref = AnnotatedRef(conf.anno_alloc.offset + sh_elements[el_ix] * Annotated_size); + uint tag = Annotated_tag(conf.anno_alloc, ref).tag; + uint seq_ix = ix - (el_ix > 0 ? sh_tile_count[el_ix - 1] : 0); + uint width = sh_tile_width[el_ix]; + uint x = sh_tile_x0[el_ix] + seq_ix % width; + uint y = sh_tile_y0[el_ix] + seq_ix / width; + bool include_tile = false; + if (tag == Annotated_BeginClip || tag == Annotated_EndClip) { + include_tile = true; + } else if (mem_ok) { + Tile tile = Tile_read(read_tile_alloc(el_ix, mem_ok), TileRef(sh_tile_base[el_ix] + (sh_tile_stride[el_ix] * y + x) * Tile_size)); + // Include the path in the tile if + // - the tile contains at least a segment (tile offset non-zero) + // - the tile is completely covered (backdrop non-zero) + include_tile = tile.tile.offset != 0 || tile.backdrop != 0; + } + if (include_tile) { + uint el_slice = el_ix / 32; + uint el_mask = 1 << (el_ix & 31); + atomicOr(sh_bitmaps[el_slice][y * N_TILE_X + x], el_mask); + } + } + + barrier(); + + // Output non-segment elements for this tile. The thread does a sequential walk + // through the non-segment elements. + uint slice_ix = 0; + uint bitmap = sh_bitmaps[0][th_ix]; + while (mem_ok) { + if (bitmap == 0) { + slice_ix++; + if (slice_ix == N_SLICE) { + break; + } + bitmap = sh_bitmaps[slice_ix][th_ix]; + if (bitmap == 0) { + continue; + } + } + uint element_ref_ix = slice_ix * 32 + findLSB(bitmap); + uint element_ix = sh_elements[element_ref_ix]; + + // Clear LSB + bitmap &= bitmap - 1; + + // At this point, we read the element again from global memory. + // If that turns out to be expensive, maybe we can pack it into + // shared memory (or perhaps just the tag). + ref = AnnotatedRef(conf.anno_alloc.offset + element_ix * Annotated_size); + AnnotatedTag tag = Annotated_tag(conf.anno_alloc, ref); + + if (clip_zero_depth == 0) { + switch (tag.tag) { + case Annotated_Color: + Tile tile = Tile_read(read_tile_alloc(element_ref_ix, mem_ok), TileRef(sh_tile_base[element_ref_ix] + + (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size)); + AnnoColor fill = Annotated_Color_read(conf.anno_alloc, ref); + if (!alloc_cmd(cmd_alloc, cmd_ref, cmd_limit)) { + break; + } + write_fill(cmd_alloc, cmd_ref, tag.flags, tile, fill.linewidth); + Cmd_Color_write(cmd_alloc, cmd_ref, CmdColor(fill.rgba_color)); + cmd_ref.offset += 4 + CmdColor_size; + break; + case Annotated_Image: + tile = Tile_read(read_tile_alloc(element_ref_ix, mem_ok), TileRef(sh_tile_base[element_ref_ix] + + (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size)); + AnnoImage fill_img = Annotated_Image_read(conf.anno_alloc, ref); + if (!alloc_cmd(cmd_alloc, cmd_ref, cmd_limit)) { + break; + } + write_fill(cmd_alloc, cmd_ref, tag.flags, tile, fill_img.linewidth); + Cmd_Image_write(cmd_alloc, cmd_ref, CmdImage(fill_img.index, fill_img.offset)); + cmd_ref.offset += 4 + CmdImage_size; + break; + case Annotated_BeginClip: + tile = Tile_read(read_tile_alloc(element_ref_ix, mem_ok), TileRef(sh_tile_base[element_ref_ix] + + (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size)); + if (tile.tile.offset == 0 && tile.backdrop == 0) { + clip_zero_depth = clip_depth + 1; + } else if (tile.tile.offset == 0 && clip_depth < 32) { + clip_one_mask |= (1 << clip_depth); + } else { + AnnoBeginClip begin_clip = Annotated_BeginClip_read(conf.anno_alloc, ref); + if (!alloc_cmd(cmd_alloc, cmd_ref, cmd_limit)) { + break; + } + write_fill(cmd_alloc, cmd_ref, tag.flags, tile, begin_clip.linewidth); + Cmd_BeginClip_write(cmd_alloc, cmd_ref); + cmd_ref.offset += 4; + if (clip_depth < 32) { + clip_one_mask &= ~(1 << clip_depth); + } + begin_slot++; + num_begin_slots = max(num_begin_slots, begin_slot); + } + clip_depth++; + break; + case Annotated_EndClip: + clip_depth--; + if (clip_depth >= 32 || (clip_one_mask & (1 << clip_depth)) == 0) { + if (!alloc_cmd(cmd_alloc, cmd_ref, cmd_limit)) { + break; + } + Cmd_Solid_write(cmd_alloc, cmd_ref); + cmd_ref.offset += 4; + begin_slot--; + Cmd_EndClip_write(cmd_alloc, cmd_ref); + cmd_ref.offset += 4; + } + break; + } + } else { + // In "clip zero" state, suppress all drawing + switch (tag.tag) { + case Annotated_BeginClip: + clip_depth++; + break; + case Annotated_EndClip: + if (clip_depth == clip_zero_depth) { + clip_zero_depth = 0; + } + clip_depth--; + break; + } + } + } + barrier(); + + rd_ix += N_TILE; + if (rd_ix >= ready_ix && partition_ix >= n_partitions) break; + } + if (bin_tile_x + tile_x < conf.width_in_tiles && bin_tile_y + tile_y < conf.height_in_tiles) { + Cmd_End_write(cmd_alloc, cmd_ref); + if (num_begin_slots > 0) { + // Write scratch allocation: one state per BeginClip per rasterizer chunk. + uint scratch_size = num_begin_slots * TILE_WIDTH_PX * TILE_HEIGHT_PX * CLIP_STATE_SIZE * 4; + MallocResult scratch = malloc(scratch_size); + // Ignore scratch.failed; we don't use the allocation and kernel4 + // checks for memory overflow before using it. + alloc_write(scratch_alloc, scratch_alloc.offset, scratch.alloc); + } + } +} diff --git a/vendor/gioui.org/shader/piet/coarse_abi.c b/vendor/gioui.org/shader/piet/coarse_abi.c new file mode 100644 index 0000000..dbecf9f --- /dev/null +++ b/vendor/gioui.org/shader/piet/coarse_abi.c @@ -0,0 +1,23 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +//go:build linux && (arm64 || arm || amd64) +// +build linux +// +build arm64 arm amd64 + +#include <stdint.h> +#include <stddef.h> +#include "abi.h" +#include "runtime.h" +#include "coarse_abi.h" + +const struct program_info coarse_program_info = { + .has_cbarriers = 1, + .min_memory_size = 100000, + .desc_set_size = sizeof(struct coarse_descriptor_set_layout), + .workgroup_size_x = 128, + .workgroup_size_y = 1, + .workgroup_size_z = 1, + .begin = coarse_coroutine_begin, + .await = coarse_coroutine_await, + .destroy = coarse_coroutine_destroy, +}; diff --git a/vendor/gioui.org/shader/piet/coarse_abi.go b/vendor/gioui.org/shader/piet/coarse_abi.go new file mode 100644 index 0000000..dfd977d --- /dev/null +++ b/vendor/gioui.org/shader/piet/coarse_abi.go @@ -0,0 +1,35 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +//go:build linux && (arm64 || arm || amd64) +// +build linux +// +build arm64 arm amd64 + +package piet + +import "gioui.org/cpu" +import "unsafe" + +/* +#cgo LDFLAGS: -lm + +#include <stdint.h> +#include <stdlib.h> +#include "abi.h" +#include "runtime.h" +#include "coarse_abi.h" +*/ +import "C" + +var CoarseProgramInfo = (*cpu.ProgramInfo)(unsafe.Pointer(&C.coarse_program_info)) + +type CoarseDescriptorSetLayout = C.struct_coarse_descriptor_set_layout + +const CoarseHash = "e7ef250c08701490aed979a889cca73943b988bdb5e4ca4b02735aebcf5e5505" + +func (l *CoarseDescriptorSetLayout) Binding0() *cpu.BufferDescriptor { + return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding0)) +} + +func (l *CoarseDescriptorSetLayout) Binding1() *cpu.BufferDescriptor { + return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding1)) +} diff --git a/vendor/gioui.org/shader/piet/coarse_abi.h b/vendor/gioui.org/shader/piet/coarse_abi.h new file mode 100644 index 0000000..24d874d --- /dev/null +++ b/vendor/gioui.org/shader/piet/coarse_abi.h @@ -0,0 +1,17 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +struct coarse_descriptor_set_layout { + struct buffer_descriptor binding0; + struct buffer_descriptor binding1; +}; + +extern coroutine coarse_coroutine_begin(struct program_data *data, + int32_t workgroupX, int32_t workgroupY, int32_t workgroupZ, + void *workgroupMemory, + int32_t firstSubgroup, + int32_t subgroupCount) ATTR_HIDDEN; + +extern bool coarse_coroutine_await(coroutine r, yield_result *res) ATTR_HIDDEN; +extern void coarse_coroutine_destroy(coroutine r) ATTR_HIDDEN; + +extern const struct program_info coarse_program_info ATTR_HIDDEN; diff --git a/vendor/gioui.org/shader/piet/coarse_abi_nosupport.go b/vendor/gioui.org/shader/piet/coarse_abi_nosupport.go new file mode 100644 index 0000000..00e2ad8 --- /dev/null +++ b/vendor/gioui.org/shader/piet/coarse_abi_nosupport.go @@ -0,0 +1,22 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +//go:build !(linux && (arm64 || arm || amd64)) +// +build !linux !arm64,!arm,!amd64 + +package piet + +import "gioui.org/cpu" + +var CoarseProgramInfo *cpu.ProgramInfo + +type CoarseDescriptorSetLayout struct{} + +const CoarseHash = "" + +func (l *CoarseDescriptorSetLayout) Binding0() *cpu.BufferDescriptor { + panic("unsupported") +} + +func (l *CoarseDescriptorSetLayout) Binding1() *cpu.BufferDescriptor { + panic("unsupported") +} diff --git a/vendor/gioui.org/shader/piet/coarse_linux_amd64.syso b/vendor/gioui.org/shader/piet/coarse_linux_amd64.syso Binary files differnew file mode 100644 index 0000000..1ac8407 --- /dev/null +++ b/vendor/gioui.org/shader/piet/coarse_linux_amd64.syso diff --git a/vendor/gioui.org/shader/piet/coarse_linux_arm.syso b/vendor/gioui.org/shader/piet/coarse_linux_arm.syso Binary files differnew file mode 100644 index 0000000..3f5ee88 --- /dev/null +++ b/vendor/gioui.org/shader/piet/coarse_linux_arm.syso diff --git a/vendor/gioui.org/shader/piet/coarse_linux_arm64.syso b/vendor/gioui.org/shader/piet/coarse_linux_arm64.syso Binary files differnew file mode 100644 index 0000000..da3b563 --- /dev/null +++ b/vendor/gioui.org/shader/piet/coarse_linux_arm64.syso diff --git a/vendor/gioui.org/shader/piet/elements.comp b/vendor/gioui.org/shader/piet/elements.comp new file mode 100644 index 0000000..17ef1ee --- /dev/null +++ b/vendor/gioui.org/shader/piet/elements.comp @@ -0,0 +1,413 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense + +// The element processing stage, first in the pipeline. +// +// This stage is primarily about applying transforms and computing bounding +// boxes. It is organized as a scan over the input elements, producing +// annotated output elements. + +#version 450 +#extension GL_GOOGLE_include_directive : enable + +#include "mem.h" +#include "setup.h" + +#define N_ROWS 4 +#define WG_SIZE 32 +#define LG_WG_SIZE 5 +#define PARTITION_SIZE (WG_SIZE * N_ROWS) + +layout(local_size_x = WG_SIZE, local_size_y = 1) in; + +layout(set = 0, binding = 1) readonly buffer ConfigBuf { + Config conf; +}; + +layout(set = 0, binding = 2) readonly buffer SceneBuf { + uint[] scene; +}; + +// It would be better to use the Vulkan memory model than +// "volatile" but shooting for compatibility here rather +// than doing things right. +layout(set = 0, binding = 3) volatile buffer StateBuf { + uint part_counter; + uint[] state; +}; + +#include "scene.h" +#include "state.h" +#include "annotated.h" +#include "pathseg.h" +#include "tile.h" + +#define StateBuf_stride (4 + 2 * State_size) + +StateRef state_aggregate_ref(uint partition_ix) { + return StateRef(4 + partition_ix * StateBuf_stride); +} + +StateRef state_prefix_ref(uint partition_ix) { + return StateRef(4 + partition_ix * StateBuf_stride + State_size); +} + +uint state_flag_index(uint partition_ix) { + return partition_ix * (StateBuf_stride / 4); +} + +// These correspond to X, A, P respectively in the prefix sum paper. +#define FLAG_NOT_READY 0 +#define FLAG_AGGREGATE_READY 1 +#define FLAG_PREFIX_READY 2 + +#define FLAG_SET_LINEWIDTH 1 +#define FLAG_SET_BBOX 2 +#define FLAG_RESET_BBOX 4 +#define FLAG_SET_FILL_MODE 8 +// Fill modes take up the next bit. Non-zero fill is 0, stroke is 1. +#define LG_FILL_MODE 4 +#define FILL_MODE_BITS 1 +#define FILL_MODE_MASK (FILL_MODE_BITS << LG_FILL_MODE) + +// This is almost like a monoid (the interaction between transformation and +// bounding boxes is approximate) +State combine_state(State a, State b) { + State c; + c.bbox.x = min(a.mat.x * b.bbox.x, a.mat.x * b.bbox.z) + min(a.mat.z * b.bbox.y, a.mat.z * b.bbox.w) + a.translate.x; + c.bbox.y = min(a.mat.y * b.bbox.x, a.mat.y * b.bbox.z) + min(a.mat.w * b.bbox.y, a.mat.w * b.bbox.w) + a.translate.y; + c.bbox.z = max(a.mat.x * b.bbox.x, a.mat.x * b.bbox.z) + max(a.mat.z * b.bbox.y, a.mat.z * b.bbox.w) + a.translate.x; + c.bbox.w = max(a.mat.y * b.bbox.x, a.mat.y * b.bbox.z) + max(a.mat.w * b.bbox.y, a.mat.w * b.bbox.w) + a.translate.y; + if ((a.flags & FLAG_RESET_BBOX) == 0 && b.bbox.z <= b.bbox.x && b.bbox.w <= b.bbox.y) { + c.bbox = a.bbox; + } else if ((a.flags & FLAG_RESET_BBOX) == 0 && (b.flags & FLAG_SET_BBOX) == 0 && + (a.bbox.z > a.bbox.x || a.bbox.w > a.bbox.y)) + { + c.bbox.xy = min(a.bbox.xy, c.bbox.xy); + c.bbox.zw = max(a.bbox.zw, c.bbox.zw); + } + // It would be more concise to cast to matrix types; ah well. + c.mat.x = a.mat.x * b.mat.x + a.mat.z * b.mat.y; + c.mat.y = a.mat.y * b.mat.x + a.mat.w * b.mat.y; + c.mat.z = a.mat.x * b.mat.z + a.mat.z * b.mat.w; + c.mat.w = a.mat.y * b.mat.z + a.mat.w * b.mat.w; + c.translate.x = a.mat.x * b.translate.x + a.mat.z * b.translate.y + a.translate.x; + c.translate.y = a.mat.y * b.translate.x + a.mat.w * b.translate.y + a.translate.y; + c.linewidth = (b.flags & FLAG_SET_LINEWIDTH) == 0 ? a.linewidth : b.linewidth; + c.flags = (a.flags & (FLAG_SET_LINEWIDTH | FLAG_SET_BBOX | FLAG_SET_FILL_MODE)) | b.flags; + c.flags |= (a.flags & FLAG_RESET_BBOX) >> 1; + uint fill_mode = (b.flags & FLAG_SET_FILL_MODE) == 0 ? a.flags : b.flags; + fill_mode &= FILL_MODE_MASK; + c.flags = (c.flags & ~FILL_MODE_MASK) | fill_mode; + c.path_count = a.path_count + b.path_count; + c.pathseg_count = a.pathseg_count + b.pathseg_count; + c.trans_count = a.trans_count + b.trans_count; + return c; +} + +State map_element(ElementRef ref) { + // TODO: it would *probably* be more efficient to make the memory read patterns less + // divergent, though it would be more wasted memory. + uint tag = Element_tag(ref).tag; + State c; + c.bbox = vec4(0.0, 0.0, 0.0, 0.0); + c.mat = vec4(1.0, 0.0, 0.0, 1.0); + c.translate = vec2(0.0, 0.0); + c.linewidth = 1.0; // TODO should be 0.0 + c.flags = 0; + c.path_count = 0; + c.pathseg_count = 0; + c.trans_count = 0; + switch (tag) { + case Element_Line: + LineSeg line = Element_Line_read(ref); + c.bbox.xy = min(line.p0, line.p1); + c.bbox.zw = max(line.p0, line.p1); + c.pathseg_count = 1; + break; + case Element_Quad: + QuadSeg quad = Element_Quad_read(ref); + c.bbox.xy = min(min(quad.p0, quad.p1), quad.p2); + c.bbox.zw = max(max(quad.p0, quad.p1), quad.p2); + c.pathseg_count = 1; + break; + case Element_Cubic: + CubicSeg cubic = Element_Cubic_read(ref); + c.bbox.xy = min(min(cubic.p0, cubic.p1), min(cubic.p2, cubic.p3)); + c.bbox.zw = max(max(cubic.p0, cubic.p1), max(cubic.p2, cubic.p3)); + c.pathseg_count = 1; + break; + case Element_FillColor: + case Element_FillImage: + case Element_BeginClip: + c.flags = FLAG_RESET_BBOX; + c.path_count = 1; + break; + case Element_EndClip: + c.path_count = 1; + break; + case Element_SetLineWidth: + SetLineWidth lw = Element_SetLineWidth_read(ref); + c.linewidth = lw.width; + c.flags = FLAG_SET_LINEWIDTH; + break; + case Element_Transform: + Transform t = Element_Transform_read(ref); + c.mat = t.mat; + c.translate = t.translate; + c.trans_count = 1; + break; + case Element_SetFillMode: + SetFillMode fm = Element_SetFillMode_read(ref); + c.flags = FLAG_SET_FILL_MODE | (fm.fill_mode << LG_FILL_MODE); + break; + } + return c; +} + +// Get the bounding box of a circle transformed by the matrix into an ellipse. +vec2 get_linewidth(State st) { + // See https://www.iquilezles.org/www/articles/ellipses/ellipses.htm + return 0.5 * st.linewidth * vec2(length(st.mat.xz), length(st.mat.yw)); +} + +shared State sh_state[WG_SIZE]; + +shared uint sh_part_ix; +shared State sh_prefix; + +void main() { + State th_state[N_ROWS]; + // Determine partition to process by atomic counter (described in Section + // 4.4 of prefix sum paper). + if (gl_LocalInvocationID.x == 0) { + sh_part_ix = atomicAdd(part_counter, 1); + } + barrier(); + uint part_ix = sh_part_ix; + + uint ix = part_ix * PARTITION_SIZE + gl_LocalInvocationID.x * N_ROWS; + ElementRef ref = ElementRef(ix * Element_size); + + th_state[0] = map_element(ref); + for (uint i = 1; i < N_ROWS; i++) { + // discussion question: would it be faster to load using more coherent patterns + // into thread memory? This is kinda strided. + th_state[i] = combine_state(th_state[i - 1], map_element(Element_index(ref, i))); + } + State agg = th_state[N_ROWS - 1]; + sh_state[gl_LocalInvocationID.x] = agg; + for (uint i = 0; i < LG_WG_SIZE; i++) { + barrier(); + if (gl_LocalInvocationID.x >= (1 << i)) { + State other = sh_state[gl_LocalInvocationID.x - (1 << i)]; + agg = combine_state(other, agg); + } + barrier(); + sh_state[gl_LocalInvocationID.x] = agg; + } + + State exclusive; + exclusive.bbox = vec4(0.0, 0.0, 0.0, 0.0); + exclusive.mat = vec4(1.0, 0.0, 0.0, 1.0); + exclusive.translate = vec2(0.0, 0.0); + exclusive.linewidth = 1.0; //TODO should be 0.0 + exclusive.flags = 0; + exclusive.path_count = 0; + exclusive.pathseg_count = 0; + exclusive.trans_count = 0; + + // Publish aggregate for this partition + if (gl_LocalInvocationID.x == WG_SIZE - 1) { + // Note: with memory model, we'd want to generate the atomic store version of this. + State_write(state_aggregate_ref(part_ix), agg); + } + memoryBarrierBuffer(); + if (gl_LocalInvocationID.x == WG_SIZE - 1) { + uint flag = FLAG_AGGREGATE_READY; + if (part_ix == 0) { + State_write(state_prefix_ref(part_ix), agg); + flag = FLAG_PREFIX_READY; + } + state[state_flag_index(part_ix)] = flag; + if (part_ix != 0) { + // step 4 of paper: decoupled lookback + uint look_back_ix = part_ix - 1; + + State their_agg; + uint their_ix = 0; + while (true) { + flag = state[state_flag_index(look_back_ix)]; + if (flag == FLAG_PREFIX_READY) { + State their_prefix = State_read(state_prefix_ref(look_back_ix)); + exclusive = combine_state(their_prefix, exclusive); + break; + } else if (flag == FLAG_AGGREGATE_READY) { + their_agg = State_read(state_aggregate_ref(look_back_ix)); + exclusive = combine_state(their_agg, exclusive); + look_back_ix--; + their_ix = 0; + continue; + } + // else spin + + // Unfortunately there's no guarantee of forward progress of other + // workgroups, so compute a bit of the aggregate before trying again. + // In the worst case, spinning stops when the aggregate is complete. + ElementRef ref = ElementRef((look_back_ix * PARTITION_SIZE + their_ix) * Element_size); + State s = map_element(ref); + if (their_ix == 0) { + their_agg = s; + } else { + their_agg = combine_state(their_agg, s); + } + their_ix++; + if (their_ix == PARTITION_SIZE) { + exclusive = combine_state(their_agg, exclusive); + if (look_back_ix == 0) { + break; + } + look_back_ix--; + their_ix = 0; + } + } + + // step 5 of paper: compute inclusive prefix + State inclusive_prefix = combine_state(exclusive, agg); + sh_prefix = exclusive; + State_write(state_prefix_ref(part_ix), inclusive_prefix); + } + } + memoryBarrierBuffer(); + if (gl_LocalInvocationID.x == WG_SIZE - 1 && part_ix != 0) { + state[state_flag_index(part_ix)] = FLAG_PREFIX_READY; + } + barrier(); + if (part_ix != 0) { + exclusive = sh_prefix; + } + + State row = exclusive; + if (gl_LocalInvocationID.x > 0) { + State other = sh_state[gl_LocalInvocationID.x - 1]; + row = combine_state(row, other); + } + for (uint i = 0; i < N_ROWS; i++) { + State st = combine_state(row, th_state[i]); + + // Here we read again from the original scene. There may be + // gains to be had from stashing in shared memory or possibly + // registers (though register pressure is an issue). + ElementRef this_ref = Element_index(ref, i); + ElementTag tag = Element_tag(this_ref); + uint fill_mode = fill_mode_from_flags(st.flags >> LG_FILL_MODE); + bool is_stroke = fill_mode == MODE_STROKE; + switch (tag.tag) { + case Element_Line: + LineSeg line = Element_Line_read(this_ref); + PathCubic path_cubic; + path_cubic.p0 = line.p0; + path_cubic.p1 = mix(line.p0, line.p1, 1.0 / 3.0); + path_cubic.p2 = mix(line.p1, line.p0, 1.0 / 3.0); + path_cubic.p3 = line.p1; + path_cubic.path_ix = st.path_count; + path_cubic.trans_ix = st.trans_count; + if (is_stroke) { + path_cubic.stroke = get_linewidth(st); + } else { + path_cubic.stroke = vec2(0.0); + } + PathSegRef path_out_ref = PathSegRef(conf.pathseg_alloc.offset + (st.pathseg_count - 1) * PathSeg_size); + PathSeg_Cubic_write(conf.pathseg_alloc, path_out_ref, fill_mode, path_cubic); + break; + case Element_Quad: + QuadSeg quad = Element_Quad_read(this_ref); + path_cubic.p0 = quad.p0; + path_cubic.p1 = mix(quad.p1, quad.p0, 1.0 / 3.0); + path_cubic.p2 = mix(quad.p1, quad.p2, 1.0 / 3.0); + path_cubic.p3 = quad.p2; + path_cubic.path_ix = st.path_count; + path_cubic.trans_ix = st.trans_count; + if (is_stroke) { + path_cubic.stroke = get_linewidth(st); + } else { + path_cubic.stroke = vec2(0.0); + } + path_out_ref = PathSegRef(conf.pathseg_alloc.offset + (st.pathseg_count - 1) * PathSeg_size); + PathSeg_Cubic_write(conf.pathseg_alloc, path_out_ref, fill_mode, path_cubic); + break; + case Element_Cubic: + CubicSeg cubic = Element_Cubic_read(this_ref); + path_cubic.p0 = cubic.p0; + path_cubic.p1 = cubic.p1; + path_cubic.p2 = cubic.p2; + path_cubic.p3 = cubic.p3; + path_cubic.path_ix = st.path_count; + path_cubic.trans_ix = st.trans_count; + if (is_stroke) { + path_cubic.stroke = get_linewidth(st); + } else { + path_cubic.stroke = vec2(0.0); + } + path_out_ref = PathSegRef(conf.pathseg_alloc.offset + (st.pathseg_count - 1) * PathSeg_size); + PathSeg_Cubic_write(conf.pathseg_alloc, path_out_ref, fill_mode, path_cubic); + break; + case Element_FillColor: + FillColor fill = Element_FillColor_read(this_ref); + AnnoColor anno_fill; + anno_fill.rgba_color = fill.rgba_color; + if (is_stroke) { + vec2 lw = get_linewidth(st); + anno_fill.bbox = st.bbox + vec4(-lw, lw); + anno_fill.linewidth = st.linewidth * sqrt(abs(st.mat.x * st.mat.w - st.mat.y * st.mat.z)); + } else { + anno_fill.bbox = st.bbox; + anno_fill.linewidth = 0.0; + } + AnnotatedRef out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size); + Annotated_Color_write(conf.anno_alloc, out_ref, fill_mode, anno_fill); + break; + case Element_FillImage: + FillImage fill_img = Element_FillImage_read(this_ref); + AnnoImage anno_img; + anno_img.index = fill_img.index; + anno_img.offset = fill_img.offset; + if (is_stroke) { + vec2 lw = get_linewidth(st); + anno_img.bbox = st.bbox + vec4(-lw, lw); + anno_img.linewidth = st.linewidth * sqrt(abs(st.mat.x * st.mat.w - st.mat.y * st.mat.z)); + } else { + anno_img.bbox = st.bbox; + anno_img.linewidth = 0.0; + } + out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size); + Annotated_Image_write(conf.anno_alloc, out_ref, fill_mode, anno_img); + break; + case Element_BeginClip: + Clip begin_clip = Element_BeginClip_read(this_ref); + AnnoBeginClip anno_begin_clip; + // This is the absolute bbox, it's been transformed during encoding. + anno_begin_clip.bbox = begin_clip.bbox; + if (is_stroke) { + vec2 lw = get_linewidth(st); + anno_begin_clip.linewidth = st.linewidth * sqrt(abs(st.mat.x * st.mat.w - st.mat.y * st.mat.z)); + } else { + anno_fill.linewidth = 0.0; + } + out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size); + Annotated_BeginClip_write(conf.anno_alloc, out_ref, fill_mode, anno_begin_clip); + break; + case Element_EndClip: + Clip end_clip = Element_EndClip_read(this_ref); + // This bbox is expected to be the same as the begin one. + AnnoEndClip anno_end_clip = AnnoEndClip(end_clip.bbox); + out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size); + Annotated_EndClip_write(conf.anno_alloc, out_ref, anno_end_clip); + break; + case Element_Transform: + TransformSeg transform = TransformSeg(st.mat, st.translate); + TransformSegRef trans_ref = TransformSegRef(conf.trans_alloc.offset + (st.trans_count - 1) * TransformSeg_size); + TransformSeg_write(conf.trans_alloc, trans_ref, transform); + break; + } + } +} diff --git a/vendor/gioui.org/shader/piet/elements_abi.c b/vendor/gioui.org/shader/piet/elements_abi.c new file mode 100644 index 0000000..bacf32d --- /dev/null +++ b/vendor/gioui.org/shader/piet/elements_abi.c @@ -0,0 +1,23 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +//go:build linux && (arm64 || arm || amd64) +// +build linux +// +build arm64 arm amd64 + +#include <stdint.h> +#include <stddef.h> +#include "abi.h" +#include "runtime.h" +#include "elements_abi.h" + +const struct program_info elements_program_info = { + .has_cbarriers = 1, + .min_memory_size = 100000, + .desc_set_size = sizeof(struct elements_descriptor_set_layout), + .workgroup_size_x = 32, + .workgroup_size_y = 1, + .workgroup_size_z = 1, + .begin = elements_coroutine_begin, + .await = elements_coroutine_await, + .destroy = elements_coroutine_destroy, +}; diff --git a/vendor/gioui.org/shader/piet/elements_abi.go b/vendor/gioui.org/shader/piet/elements_abi.go new file mode 100644 index 0000000..a85eeca --- /dev/null +++ b/vendor/gioui.org/shader/piet/elements_abi.go @@ -0,0 +1,43 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +//go:build linux && (arm64 || arm || amd64) +// +build linux +// +build arm64 arm amd64 + +package piet + +import "gioui.org/cpu" +import "unsafe" + +/* +#cgo LDFLAGS: -lm + +#include <stdint.h> +#include <stdlib.h> +#include "abi.h" +#include "runtime.h" +#include "elements_abi.h" +*/ +import "C" + +var ElementsProgramInfo = (*cpu.ProgramInfo)(unsafe.Pointer(&C.elements_program_info)) + +type ElementsDescriptorSetLayout = C.struct_elements_descriptor_set_layout + +const ElementsHash = "0f18de15866045b36217068789c9c8715a63e0f9f120c53ea2d4d76f53e443c3" + +func (l *ElementsDescriptorSetLayout) Binding0() *cpu.BufferDescriptor { + return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding0)) +} + +func (l *ElementsDescriptorSetLayout) Binding1() *cpu.BufferDescriptor { + return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding1)) +} + +func (l *ElementsDescriptorSetLayout) Binding2() *cpu.BufferDescriptor { + return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding2)) +} + +func (l *ElementsDescriptorSetLayout) Binding3() *cpu.BufferDescriptor { + return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding3)) +} diff --git a/vendor/gioui.org/shader/piet/elements_abi.h b/vendor/gioui.org/shader/piet/elements_abi.h new file mode 100644 index 0000000..c455224 --- /dev/null +++ b/vendor/gioui.org/shader/piet/elements_abi.h @@ -0,0 +1,19 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +struct elements_descriptor_set_layout { + struct buffer_descriptor binding0; + struct buffer_descriptor binding1; + struct buffer_descriptor binding2; + struct buffer_descriptor binding3; +}; + +extern coroutine elements_coroutine_begin(struct program_data *data, + int32_t workgroupX, int32_t workgroupY, int32_t workgroupZ, + void *workgroupMemory, + int32_t firstSubgroup, + int32_t subgroupCount) ATTR_HIDDEN; + +extern bool elements_coroutine_await(coroutine r, yield_result *res) ATTR_HIDDEN; +extern void elements_coroutine_destroy(coroutine r) ATTR_HIDDEN; + +extern const struct program_info elements_program_info ATTR_HIDDEN; diff --git a/vendor/gioui.org/shader/piet/elements_abi_nosupport.go b/vendor/gioui.org/shader/piet/elements_abi_nosupport.go new file mode 100644 index 0000000..8b30234 --- /dev/null +++ b/vendor/gioui.org/shader/piet/elements_abi_nosupport.go @@ -0,0 +1,30 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +//go:build !(linux && (arm64 || arm || amd64)) +// +build !linux !arm64,!arm,!amd64 + +package piet + +import "gioui.org/cpu" + +var ElementsProgramInfo *cpu.ProgramInfo + +type ElementsDescriptorSetLayout struct{} + +const ElementsHash = "" + +func (l *ElementsDescriptorSetLayout) Binding0() *cpu.BufferDescriptor { + panic("unsupported") +} + +func (l *ElementsDescriptorSetLayout) Binding1() *cpu.BufferDescriptor { + panic("unsupported") +} + +func (l *ElementsDescriptorSetLayout) Binding2() *cpu.BufferDescriptor { + panic("unsupported") +} + +func (l *ElementsDescriptorSetLayout) Binding3() *cpu.BufferDescriptor { + panic("unsupported") +} diff --git a/vendor/gioui.org/shader/piet/elements_linux_amd64.syso b/vendor/gioui.org/shader/piet/elements_linux_amd64.syso Binary files differnew file mode 100644 index 0000000..bba953b --- /dev/null +++ b/vendor/gioui.org/shader/piet/elements_linux_amd64.syso diff --git a/vendor/gioui.org/shader/piet/elements_linux_arm.syso b/vendor/gioui.org/shader/piet/elements_linux_arm.syso Binary files differnew file mode 100644 index 0000000..1325ac8 --- /dev/null +++ b/vendor/gioui.org/shader/piet/elements_linux_arm.syso diff --git a/vendor/gioui.org/shader/piet/elements_linux_arm64.syso b/vendor/gioui.org/shader/piet/elements_linux_arm64.syso Binary files differnew file mode 100644 index 0000000..6855eb6 --- /dev/null +++ b/vendor/gioui.org/shader/piet/elements_linux_arm64.syso diff --git a/vendor/gioui.org/shader/piet/gen.go b/vendor/gioui.org/shader/piet/gen.go new file mode 100644 index 0000000..4b80e92 --- /dev/null +++ b/vendor/gioui.org/shader/piet/gen.go @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package piet + +//go:generate go run ../cmd/convertshaders -package piet -dir . diff --git a/vendor/gioui.org/shader/piet/gencpu.sh b/vendor/gioui.org/shader/piet/gencpu.sh new file mode 100644 index 0000000..d6f6f7d --- /dev/null +++ b/vendor/gioui.org/shader/piet/gencpu.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +# SPDX-License-Identifier: Unlicense OR MIT + +set -e + +OBJCOPY_ARM64=$ANDROID_SDK_ROOT/ndk/21.3.6528147/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/aarch64-linux-android/bin/objcopy +OBJCOPY_ARM=$ANDROID_SDK_ROOT/ndk/21.3.6528147/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/arm-linux-androideabi/bin/objcopy +SWIFTSHADER=$HOME/.cache/swiftshader + +export CGO_ENABLED=1 +export GOARCH=386 +export VK_ICD_FILENAMES=$SWIFTSHADER/build.32bit/Linux/vk_swiftshader_icd.json + +export SWIFTSHADER_TRIPLE=armv7a-none-eabi +go run gioui.org/cpu/cmd/compile -arch arm -objcopy $OBJCOPY_ARM -layout "0:buffer,1:buffer,2:image,3:image" kernel4.comp +go run gioui.org/cpu/cmd/compile -arch arm -objcopy $OBJCOPY_ARM -layout "0:buffer,1:buffer" coarse.comp +go run gioui.org/cpu/cmd/compile -arch arm -objcopy $OBJCOPY_ARM -layout "0:buffer,1:buffer" binning.comp +go run gioui.org/cpu/cmd/compile -arch arm -objcopy $OBJCOPY_ARM -layout "0:buffer,1:buffer" backdrop.comp +go run gioui.org/cpu/cmd/compile -arch arm -objcopy $OBJCOPY_ARM -layout "0:buffer,1:buffer" path_coarse.comp +go run gioui.org/cpu/cmd/compile -arch arm -objcopy $OBJCOPY_ARM -layout "0:buffer,1:buffer" tile_alloc.comp +go run gioui.org/cpu/cmd/compile -arch arm -objcopy $OBJCOPY_ARM -layout "0:buffer,1:buffer,2:buffer,3:buffer" elements.comp + +export GOARCH=amd64 +export VK_ICD_FILENAMES=$SWIFTSHADER/build.64bit/Linux/vk_swiftshader_icd.json +export SWIFTSHADER_TRIPLE=x86_64-unknown-none-gnu + +go run gioui.org/cpu/cmd/compile -arch amd64 -layout "0:buffer,1:buffer,2:image,3:image" kernel4.comp +go run gioui.org/cpu/cmd/compile -arch amd64 -layout "0:buffer,1:buffer" coarse.comp +go run gioui.org/cpu/cmd/compile -arch amd64 -layout "0:buffer,1:buffer" binning.comp +go run gioui.org/cpu/cmd/compile -arch amd64 -layout "0:buffer,1:buffer" backdrop.comp +go run gioui.org/cpu/cmd/compile -arch amd64 -layout "0:buffer,1:buffer" path_coarse.comp +go run gioui.org/cpu/cmd/compile -arch amd64 -layout "0:buffer,1:buffer" tile_alloc.comp +go run gioui.org/cpu/cmd/compile -arch amd64 -layout "0:buffer,1:buffer,2:buffer,3:buffer" elements.comp + +export SWIFTSHADER_TRIPLE=aarch64-unknown-linux-gnu + +go run gioui.org/cpu/cmd/compile -arch arm64 -objcopy $OBJCOPY_ARM64 -layout "0:buffer,1:buffer,2:image,3:image" kernel4.comp +go run gioui.org/cpu/cmd/compile -arch arm64 -objcopy $OBJCOPY_ARM64 -layout "0:buffer,1:buffer" coarse.comp +go run gioui.org/cpu/cmd/compile -arch arm64 -objcopy $OBJCOPY_ARM64 -layout "0:buffer,1:buffer" binning.comp +go run gioui.org/cpu/cmd/compile -arch arm64 -objcopy $OBJCOPY_ARM64 -layout "0:buffer,1:buffer" backdrop.comp +go run gioui.org/cpu/cmd/compile -arch arm64 -objcopy $OBJCOPY_ARM64 -layout "0:buffer,1:buffer" path_coarse.comp +go run gioui.org/cpu/cmd/compile -arch arm64 -objcopy $OBJCOPY_ARM64 -layout "0:buffer,1:buffer" tile_alloc.comp +go run gioui.org/cpu/cmd/compile -arch arm64 -objcopy $OBJCOPY_ARM64 -layout "0:buffer,1:buffer,2:buffer,3:buffer" elements.comp diff --git a/vendor/gioui.org/shader/piet/kernel4.comp b/vendor/gioui.org/shader/piet/kernel4.comp new file mode 100644 index 0000000..3f8ef65 --- /dev/null +++ b/vendor/gioui.org/shader/piet/kernel4.comp @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense + +// This is "kernel 4" in a 4-kernel pipeline. It renders the commands +// in the per-tile command list to an image. + +// Right now, this kernel stores the image in a buffer, but a better +// plan is to use a texture. This is because of limited support. + +#version 450 +#extension GL_GOOGLE_include_directive : enable +#ifdef ENABLE_IMAGE_INDICES +#extension GL_EXT_nonuniform_qualifier : enable +#endif + +#include "mem.h" +#include "setup.h" + +#define CHUNK_X 2 +#define CHUNK_Y 4 +#define CHUNK CHUNK_X * CHUNK_Y +#define CHUNK_DX (TILE_WIDTH_PX / CHUNK_X) +#define CHUNK_DY (TILE_HEIGHT_PX / CHUNK_Y) +layout(local_size_x = CHUNK_DX, local_size_y = CHUNK_DY) in; + +layout(set = 0, binding = 1) restrict readonly buffer ConfigBuf { + Config conf; +}; + +layout(rgba8, set = 0, binding = 2) uniform restrict writeonly image2D image; + +#ifdef ENABLE_IMAGE_INDICES +layout(rgba8, set = 0, binding = 3) uniform restrict readonly image2D images[]; +#else +layout(rgba8, set = 0, binding = 3) uniform restrict readonly image2D images; +#endif + +#include "ptcl.h" +#include "tile.h" + +mediump vec3 tosRGB(mediump vec3 rgb) { + bvec3 cutoff = greaterThanEqual(rgb, vec3(0.0031308)); + mediump vec3 below = vec3(12.92)*rgb; + mediump vec3 above = vec3(1.055)*pow(rgb, vec3(0.41666)) - vec3(0.055); + return mix(below, above, cutoff); +} + +mediump vec3 fromsRGB(mediump vec3 srgb) { + // Formula from EXT_sRGB. + bvec3 cutoff = greaterThanEqual(srgb, vec3(0.04045)); + mediump vec3 below = srgb/vec3(12.92); + mediump vec3 above = pow((srgb + vec3(0.055))/vec3(1.055), vec3(2.4)); + return mix(below, above, cutoff); +} + +// unpacksRGB unpacks a color in the sRGB color space to a vec4 in the linear color +// space. +mediump vec4 unpacksRGB(uint srgba) { + mediump vec4 color = unpackUnorm4x8(srgba).wzyx; + return vec4(fromsRGB(color.rgb), color.a); +} + +// packsRGB packs a color in the linear color space into its 8-bit sRGB equivalent. +uint packsRGB(mediump vec4 rgba) { + rgba = vec4(tosRGB(rgba.rgb), rgba.a); + return packUnorm4x8(rgba.wzyx); +} + +uvec2 chunk_offset(uint i) { + return uvec2(i % CHUNK_X * CHUNK_DX, i / CHUNK_X * CHUNK_DY); +} + +mediump vec4[CHUNK] fillImage(uvec2 xy, CmdImage cmd_img) { + mediump vec4 rgba[CHUNK]; + for (uint i = 0; i < CHUNK; i++) { + ivec2 uv = ivec2(xy + chunk_offset(i)) + cmd_img.offset; + mediump vec4 fg_rgba; +#ifdef ENABLE_IMAGE_INDICES + fg_rgba = imageLoad(images[cmd_img.index], uv); +#else + fg_rgba = imageLoad(images, uv); +#endif + fg_rgba.rgb = fromsRGB(fg_rgba.rgb); + rgba[i] = fg_rgba; + } + return rgba; +} + +void main() { + uint tile_ix = gl_WorkGroupID.y * conf.width_in_tiles + gl_WorkGroupID.x; + Alloc cmd_alloc = slice_mem(conf.ptcl_alloc, tile_ix * PTCL_INITIAL_ALLOC, PTCL_INITIAL_ALLOC); + CmdRef cmd_ref = CmdRef(cmd_alloc.offset); + + // Read scrach space allocation, written first in the command list. + Alloc scratch_alloc = alloc_read(cmd_alloc, cmd_ref.offset); + cmd_ref.offset += Alloc_size; + + uvec2 xy_uint = uvec2(gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_WorkGroupID.x, gl_LocalInvocationID.y + TILE_HEIGHT_PX * gl_WorkGroupID.y); + vec2 xy = vec2(xy_uint); + mediump vec4 rgba[CHUNK]; + for (uint i = 0; i < CHUNK; i++) { + rgba[i] = vec4(0.0); + // TODO: remove this debug image support when the actual image method is plumbed. +#ifdef DEBUG_IMAGES +#ifdef ENABLE_IMAGE_INDICES + if (xy_uint.x < 1024 && xy_uint.y < 1024) { + rgba[i] = imageLoad(images[gl_WorkGroupID.x / 64], ivec2(xy_uint + chunk_offset(i))/4); + } +#else + if (xy_uint.x < 1024 && xy_uint.y < 1024) { + rgb[i] = imageLoad(images[0], ivec2(xy_uint + chunk_offset(i))/4).rgb; + } +#endif +#endif + } + + mediump float area[CHUNK]; + uint clip_depth = 0; + bool mem_ok = mem_error == NO_ERROR; + while (mem_ok) { + uint tag = Cmd_tag(cmd_alloc, cmd_ref).tag; + if (tag == Cmd_End) { + break; + } + switch (tag) { + case Cmd_Stroke: + // Calculate distance field from all the line segments in this tile. + CmdStroke stroke = Cmd_Stroke_read(cmd_alloc, cmd_ref); + mediump float df[CHUNK]; + for (uint k = 0; k < CHUNK; k++) df[k] = 1e9; + TileSegRef tile_seg_ref = TileSegRef(stroke.tile_ref); + do { + TileSeg seg = TileSeg_read(new_alloc(tile_seg_ref.offset, TileSeg_size, mem_ok), tile_seg_ref); + vec2 line_vec = seg.vector; + for (uint k = 0; k < CHUNK; k++) { + vec2 dpos = xy + vec2(0.5, 0.5) - seg.origin; + dpos += vec2(chunk_offset(k)); + float t = clamp(dot(line_vec, dpos) / dot(line_vec, line_vec), 0.0, 1.0); + df[k] = min(df[k], length(line_vec * t - dpos)); + } + tile_seg_ref = seg.next; + } while (tile_seg_ref.offset != 0); + for (uint k = 0; k < CHUNK; k++) { + area[k] = clamp(stroke.half_width + 0.5 - df[k], 0.0, 1.0); + } + cmd_ref.offset += 4 + CmdStroke_size; + break; + case Cmd_Fill: + CmdFill fill = Cmd_Fill_read(cmd_alloc, cmd_ref); + for (uint k = 0; k < CHUNK; k++) area[k] = float(fill.backdrop); + tile_seg_ref = TileSegRef(fill.tile_ref); + // Calculate coverage based on backdrop + coverage of each line segment + do { + TileSeg seg = TileSeg_read(new_alloc(tile_seg_ref.offset, TileSeg_size, mem_ok), tile_seg_ref); + for (uint k = 0; k < CHUNK; k++) { + vec2 my_xy = xy + vec2(chunk_offset(k)); + vec2 start = seg.origin - my_xy; + vec2 end = start + seg.vector; + vec2 window = clamp(vec2(start.y, end.y), 0.0, 1.0); + if (window.x != window.y) { + vec2 t = (window - start.y) / seg.vector.y; + vec2 xs = vec2(mix(start.x, end.x, t.x), mix(start.x, end.x, t.y)); + float xmin = min(min(xs.x, xs.y), 1.0) - 1e-6; + float xmax = max(xs.x, xs.y); + float b = min(xmax, 1.0); + float c = max(b, 0.0); + float d = max(xmin, 0.0); + float a = (b + 0.5 * (d * d - c * c) - xmin) / (xmax - xmin); + area[k] += a * (window.x - window.y); + } + area[k] += sign(seg.vector.x) * clamp(my_xy.y - seg.y_edge + 1.0, 0.0, 1.0); + } + tile_seg_ref = seg.next; + } while (tile_seg_ref.offset != 0); + for (uint k = 0; k < CHUNK; k++) { + area[k] = min(abs(area[k]), 1.0); + } + cmd_ref.offset += 4 + CmdFill_size; + break; + case Cmd_Solid: + for (uint k = 0; k < CHUNK; k++) { + area[k] = 1.0; + } + cmd_ref.offset += 4; + break; + case Cmd_Alpha: + CmdAlpha alpha = Cmd_Alpha_read(cmd_alloc, cmd_ref); + for (uint k = 0; k < CHUNK; k++) { + area[k] = alpha.alpha; + } + cmd_ref.offset += 4 + CmdAlpha_size; + break; + case Cmd_Color: + CmdColor color = Cmd_Color_read(cmd_alloc, cmd_ref); + mediump vec4 fg = unpacksRGB(color.rgba_color); + for (uint k = 0; k < CHUNK; k++) { + mediump vec4 fg_k = fg * area[k]; + rgba[k] = rgba[k] * (1.0 - fg_k.a) + fg_k; + } + cmd_ref.offset += 4 + CmdColor_size; + break; + case Cmd_Image: + CmdImage fill_img = Cmd_Image_read(cmd_alloc, cmd_ref); + mediump vec4 img[CHUNK] = fillImage(xy_uint, fill_img); + for (uint k = 0; k < CHUNK; k++) { + mediump vec4 fg_k = img[k] * area[k]; + rgba[k] = rgba[k] * (1.0 - fg_k.a) + fg_k; + } + cmd_ref.offset += 4 + CmdImage_size; + break; + case Cmd_BeginClip: + uint base_ix = (scratch_alloc.offset >> 2) + CLIP_STATE_SIZE * (clip_depth * TILE_WIDTH_PX * TILE_HEIGHT_PX + + gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_LocalInvocationID.y); + for (uint k = 0; k < CHUNK; k++) { + uvec2 offset = chunk_offset(k); + uint srgb = packsRGB(vec4(rgba[k])); + mediump float alpha = clamp(abs(area[k]), 0.0, 1.0); + write_mem(scratch_alloc, base_ix + 0 + CLIP_STATE_SIZE * (offset.x + offset.y * TILE_WIDTH_PX), srgb); + write_mem(scratch_alloc, base_ix + 1 + CLIP_STATE_SIZE * (offset.x + offset.y * TILE_WIDTH_PX), floatBitsToUint(alpha)); + rgba[k] = vec4(0.0); + } + clip_depth++; + cmd_ref.offset += 4; + break; + case Cmd_EndClip: + clip_depth--; + base_ix = (scratch_alloc.offset >> 2) + CLIP_STATE_SIZE * (clip_depth * TILE_WIDTH_PX * TILE_HEIGHT_PX + + gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_LocalInvocationID.y); + for (uint k = 0; k < CHUNK; k++) { + uvec2 offset = chunk_offset(k); + uint srgb = read_mem(scratch_alloc, base_ix + 0 + CLIP_STATE_SIZE * (offset.x + offset.y * TILE_WIDTH_PX)); + uint alpha = read_mem(scratch_alloc, base_ix + 1 + CLIP_STATE_SIZE * (offset.x + offset.y * TILE_WIDTH_PX)); + mediump vec4 bg = unpacksRGB(srgb); + mediump vec4 fg = rgba[k] * area[k] * uintBitsToFloat(alpha); + rgba[k] = bg * (1.0 - fg.a) + fg; + } + cmd_ref.offset += 4; + break; + case Cmd_Jump: + cmd_ref = CmdRef(Cmd_Jump_read(cmd_alloc, cmd_ref).new_ref); + cmd_alloc.offset = cmd_ref.offset; + break; + } + } + + for (uint i = 0; i < CHUNK; i++) { + imageStore(image, ivec2(xy_uint + chunk_offset(i)), vec4(tosRGB(rgba[i].rgb), rgba[i].a)); + } +} diff --git a/vendor/gioui.org/shader/piet/kernel4_abi.c b/vendor/gioui.org/shader/piet/kernel4_abi.c new file mode 100644 index 0000000..81c4ac4 --- /dev/null +++ b/vendor/gioui.org/shader/piet/kernel4_abi.c @@ -0,0 +1,23 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +//go:build linux && (arm64 || arm || amd64) +// +build linux +// +build arm64 arm amd64 + +#include <stdint.h> +#include <stddef.h> +#include "abi.h" +#include "runtime.h" +#include "kernel4_abi.h" + +const struct program_info kernel4_program_info = { + .has_cbarriers = 0, + .min_memory_size = 100000, + .desc_set_size = sizeof(struct kernel4_descriptor_set_layout), + .workgroup_size_x = 16, + .workgroup_size_y = 8, + .workgroup_size_z = 1, + .begin = kernel4_coroutine_begin, + .await = kernel4_coroutine_await, + .destroy = kernel4_coroutine_destroy, +}; diff --git a/vendor/gioui.org/shader/piet/kernel4_abi.go b/vendor/gioui.org/shader/piet/kernel4_abi.go new file mode 100644 index 0000000..9804647 --- /dev/null +++ b/vendor/gioui.org/shader/piet/kernel4_abi.go @@ -0,0 +1,43 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +//go:build linux && (arm64 || arm || amd64) +// +build linux +// +build arm64 arm amd64 + +package piet + +import "gioui.org/cpu" +import "unsafe" + +/* +#cgo LDFLAGS: -lm + +#include <stdint.h> +#include <stdlib.h> +#include "abi.h" +#include "runtime.h" +#include "kernel4_abi.h" +*/ +import "C" + +var Kernel4ProgramInfo = (*cpu.ProgramInfo)(unsafe.Pointer(&C.kernel4_program_info)) + +type Kernel4DescriptorSetLayout = C.struct_kernel4_descriptor_set_layout + +const Kernel4Hash = "88ae29cf53c1819fad9680e85faaee30fcc934d1a978a717695c966ef051bf1d" + +func (l *Kernel4DescriptorSetLayout) Binding0() *cpu.BufferDescriptor { + return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding0)) +} + +func (l *Kernel4DescriptorSetLayout) Binding1() *cpu.BufferDescriptor { + return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding1)) +} + +func (l *Kernel4DescriptorSetLayout) Binding2() *cpu.ImageDescriptor { + return (*cpu.ImageDescriptor)(unsafe.Pointer(&l.binding2)) +} + +func (l *Kernel4DescriptorSetLayout) Binding3() *cpu.ImageDescriptor { + return (*cpu.ImageDescriptor)(unsafe.Pointer(&l.binding3)) +} diff --git a/vendor/gioui.org/shader/piet/kernel4_abi.h b/vendor/gioui.org/shader/piet/kernel4_abi.h new file mode 100644 index 0000000..0d3b4c9 --- /dev/null +++ b/vendor/gioui.org/shader/piet/kernel4_abi.h @@ -0,0 +1,19 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +struct kernel4_descriptor_set_layout { + struct buffer_descriptor binding0; + struct buffer_descriptor binding1; + struct image_descriptor binding2; + struct image_descriptor binding3; +}; + +extern coroutine kernel4_coroutine_begin(struct program_data *data, + int32_t workgroupX, int32_t workgroupY, int32_t workgroupZ, + void *workgroupMemory, + int32_t firstSubgroup, + int32_t subgroupCount) ATTR_HIDDEN; + +extern bool kernel4_coroutine_await(coroutine r, yield_result *res) ATTR_HIDDEN; +extern void kernel4_coroutine_destroy(coroutine r) ATTR_HIDDEN; + +extern const struct program_info kernel4_program_info ATTR_HIDDEN; diff --git a/vendor/gioui.org/shader/piet/kernel4_abi_nosupport.go b/vendor/gioui.org/shader/piet/kernel4_abi_nosupport.go new file mode 100644 index 0000000..6cff47f --- /dev/null +++ b/vendor/gioui.org/shader/piet/kernel4_abi_nosupport.go @@ -0,0 +1,30 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +//go:build !(linux && (arm64 || arm || amd64)) +// +build !linux !arm64,!arm,!amd64 + +package piet + +import "gioui.org/cpu" + +var Kernel4ProgramInfo *cpu.ProgramInfo + +type Kernel4DescriptorSetLayout struct{} + +const Kernel4Hash = "" + +func (l *Kernel4DescriptorSetLayout) Binding0() *cpu.BufferDescriptor { + panic("unsupported") +} + +func (l *Kernel4DescriptorSetLayout) Binding1() *cpu.BufferDescriptor { + panic("unsupported") +} + +func (l *Kernel4DescriptorSetLayout) Binding2() *cpu.ImageDescriptor { + panic("unsupported") +} + +func (l *Kernel4DescriptorSetLayout) Binding3() *cpu.ImageDescriptor { + panic("unsupported") +} diff --git a/vendor/gioui.org/shader/piet/kernel4_linux_amd64.syso b/vendor/gioui.org/shader/piet/kernel4_linux_amd64.syso Binary files differnew file mode 100644 index 0000000..bdf0e46 --- /dev/null +++ b/vendor/gioui.org/shader/piet/kernel4_linux_amd64.syso diff --git a/vendor/gioui.org/shader/piet/kernel4_linux_arm.syso b/vendor/gioui.org/shader/piet/kernel4_linux_arm.syso Binary files differnew file mode 100644 index 0000000..6fab185 --- /dev/null +++ b/vendor/gioui.org/shader/piet/kernel4_linux_arm.syso diff --git a/vendor/gioui.org/shader/piet/kernel4_linux_arm64.syso b/vendor/gioui.org/shader/piet/kernel4_linux_arm64.syso Binary files differnew file mode 100644 index 0000000..b7994d7 --- /dev/null +++ b/vendor/gioui.org/shader/piet/kernel4_linux_arm64.syso diff --git a/vendor/gioui.org/shader/piet/mem.h b/vendor/gioui.org/shader/piet/mem.h new file mode 100644 index 0000000..9e81f04 --- /dev/null +++ b/vendor/gioui.org/shader/piet/mem.h @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense + +layout(set = 0, binding = 0) buffer Memory { + // offset into memory of the next allocation, initialized by the user. + uint mem_offset; + // mem_error tracks the status of memory accesses, initialized to NO_ERROR + // by the user. ERR_MALLOC_FAILED is reported for insufficient memory. + // If MEM_DEBUG is defined the following errors are reported: + // - ERR_OUT_OF_BOUNDS is reported for out of bounds writes. + // - ERR_UNALIGNED_ACCESS for memory access not aligned to 32-bit words. + uint mem_error; + uint[] memory; +}; + +// Uncomment this line to add the size field to Alloc and enable memory checks. +// Note that the Config struct in setup.h grows size fields as well. +//#define MEM_DEBUG + +#define NO_ERROR 0 +#define ERR_MALLOC_FAILED 1 +#define ERR_OUT_OF_BOUNDS 2 +#define ERR_UNALIGNED_ACCESS 3 + +#ifdef MEM_DEBUG +#define Alloc_size 16 +#else +#define Alloc_size 8 +#endif + +// Alloc represents a memory allocation. +struct Alloc { + // offset in bytes into memory. + uint offset; +#ifdef MEM_DEBUG + // size in bytes of the allocation. + uint size; +#endif +}; + +struct MallocResult { + Alloc alloc; + // failed is true if the allocation overflowed memory. + bool failed; +}; + +// new_alloc synthesizes an Alloc from an offset and size. +Alloc new_alloc(uint offset, uint size, bool mem_ok) { + Alloc a; + a.offset = offset; +#ifdef MEM_DEBUG + if (mem_ok) { + a.size = size; + } else { + a.size = 0; + } +#endif + return a; +} + +// malloc allocates size bytes of memory. +MallocResult malloc(uint size) { + MallocResult r; + uint offset = atomicAdd(mem_offset, size); + r.failed = offset + size > memory.length() * 4; + r.alloc = new_alloc(offset, size, !r.failed); + if (r.failed) { + atomicMax(mem_error, ERR_MALLOC_FAILED); + return r; + } +#ifdef MEM_DEBUG + if ((size & 3) != 0) { + r.failed = true; + atomicMax(mem_error, ERR_UNALIGNED_ACCESS); + return r; + } +#endif + return r; +} + +// touch_mem checks whether access to the memory word at offset is valid. +// If MEM_DEBUG is defined, touch_mem returns false if offset is out of bounds. +// Offset is in words. +bool touch_mem(Alloc alloc, uint offset) { +#ifdef MEM_DEBUG + if (offset < alloc.offset/4 || offset >= (alloc.offset + alloc.size)/4) { + atomicMax(mem_error, ERR_OUT_OF_BOUNDS); + return false; + } +#endif + return true; +} + +// write_mem writes val to memory at offset. +// Offset is in words. +void write_mem(Alloc alloc, uint offset, uint val) { + if (!touch_mem(alloc, offset)) { + return; + } + memory[offset] = val; +} + +// read_mem reads the value from memory at offset. +// Offset is in words. +uint read_mem(Alloc alloc, uint offset) { + if (!touch_mem(alloc, offset)) { + return 0; + } + uint v = memory[offset]; + return v; +} + +// slice_mem returns a sub-allocation inside another. Offset and size are in +// bytes, relative to a.offset. +Alloc slice_mem(Alloc a, uint offset, uint size) { +#ifdef MEM_DEBUG + if ((offset & 3) != 0 || (size & 3) != 0) { + atomicMax(mem_error, ERR_UNALIGNED_ACCESS); + return Alloc(0, 0); + } + if (offset + size > a.size) { + // slice_mem is sometimes used for slices outside bounds, + // but never written. + return Alloc(0, 0); + } + return Alloc(a.offset + offset, size); +#else + return Alloc(a.offset + offset); +#endif +} + +// alloc_write writes alloc to memory at offset bytes. +void alloc_write(Alloc a, uint offset, Alloc alloc) { + write_mem(a, offset >> 2, alloc.offset); +#ifdef MEM_DEBUG + write_mem(a, (offset >> 2) + 1, alloc.size); +#endif +} + +// alloc_read reads an Alloc from memory at offset bytes. +Alloc alloc_read(Alloc a, uint offset) { + Alloc alloc; + alloc.offset = read_mem(a, offset >> 2); +#ifdef MEM_DEBUG + alloc.size = read_mem(a, (offset >> 2) + 1); +#endif + return alloc; +} diff --git a/vendor/gioui.org/shader/piet/path_coarse.comp b/vendor/gioui.org/shader/piet/path_coarse.comp new file mode 100644 index 0000000..ea525f5 --- /dev/null +++ b/vendor/gioui.org/shader/piet/path_coarse.comp @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense + +// Coarse rasterization of path segments. + +// Allocation and initialization of tiles for paths. + +#version 450 +#extension GL_GOOGLE_include_directive : enable + +#include "mem.h" +#include "setup.h" + +#define LG_COARSE_WG 5 +#define COARSE_WG (1 << LG_COARSE_WG) + +layout(local_size_x = COARSE_WG, local_size_y = 1) in; + +layout(set = 0, binding = 1) readonly buffer ConfigBuf { + Config conf; +}; + +#include "pathseg.h" +#include "tile.h" + +// scale factors useful for converting coordinates to tiles +#define SX (1.0 / float(TILE_WIDTH_PX)) +#define SY (1.0 / float(TILE_HEIGHT_PX)) + +#define ACCURACY 0.25 +#define Q_ACCURACY (ACCURACY * 0.1) +#define REM_ACCURACY (ACCURACY - Q_ACCURACY) +#define MAX_HYPOT2 (432.0 * Q_ACCURACY * Q_ACCURACY) +#define MAX_QUADS 16 + +vec2 eval_quad(vec2 p0, vec2 p1, vec2 p2, float t) { + float mt = 1.0 - t; + return p0 * (mt * mt) + (p1 * (mt * 2.0) + p2 * t) * t; +} + +vec2 eval_cubic(vec2 p0, vec2 p1, vec2 p2, vec2 p3, float t) { + float mt = 1.0 - t; + return p0 * (mt * mt * mt) + (p1 * (mt * mt * 3.0) + (p2 * (mt * 3.0) + p3 * t) * t) * t; +} + +struct SubdivResult { + float val; + float a0; + float a2; +}; + +/// An approximation to $\int (1 + 4x^2) ^ -0.25 dx$ +/// +/// This is used for flattening curves. +#define D 0.67 +float approx_parabola_integral(float x) { + return x * inversesqrt(sqrt(1.0 - D + (D * D * D * D + 0.25 * x * x))); +} + +/// An approximation to the inverse parabola integral. +#define B 0.39 +float approx_parabola_inv_integral(float x) { + return x * sqrt(1.0 - B + (B * B + 0.25 * x * x)); +} + +SubdivResult estimate_subdiv(vec2 p0, vec2 p1, vec2 p2, float sqrt_tol) { + vec2 d01 = p1 - p0; + vec2 d12 = p2 - p1; + vec2 dd = d01 - d12; + float cross = (p2.x - p0.x) * dd.y - (p2.y - p0.y) * dd.x; + float x0 = (d01.x * dd.x + d01.y * dd.y) / cross; + float x2 = (d12.x * dd.x + d12.y * dd.y) / cross; + float scale = abs(cross / (length(dd) * (x2 - x0))); + + float a0 = approx_parabola_integral(x0); + float a2 = approx_parabola_integral(x2); + float val = 0.0; + if (scale < 1e9) { + float da = abs(a2 - a0); + float sqrt_scale = sqrt(scale); + if (sign(x0) == sign(x2)) { + val = da * sqrt_scale; + } else { + float xmin = sqrt_tol / sqrt_scale; + val = sqrt_tol * da / approx_parabola_integral(xmin); + } + } + return SubdivResult(val, a0, a2); +} + +void main() { + uint element_ix = gl_GlobalInvocationID.x; + PathSegRef ref = PathSegRef(conf.pathseg_alloc.offset + element_ix * PathSeg_size); + + PathSegTag tag = PathSegTag(PathSeg_Nop, 0); + if (element_ix < conf.n_pathseg) { + tag = PathSeg_tag(conf.pathseg_alloc, ref); + } + bool mem_ok = mem_error == NO_ERROR; + switch (tag.tag) { + case PathSeg_Cubic: + PathCubic cubic = PathSeg_Cubic_read(conf.pathseg_alloc, ref); + + uint trans_ix = cubic.trans_ix; + if (trans_ix > 0) { + TransformSegRef trans_ref = TransformSegRef(conf.trans_alloc.offset + (trans_ix - 1) * TransformSeg_size); + TransformSeg trans = TransformSeg_read(conf.trans_alloc, trans_ref); + cubic.p0 = trans.mat.xy * cubic.p0.x + trans.mat.zw * cubic.p0.y + trans.translate; + cubic.p1 = trans.mat.xy * cubic.p1.x + trans.mat.zw * cubic.p1.y + trans.translate; + cubic.p2 = trans.mat.xy * cubic.p2.x + trans.mat.zw * cubic.p2.y + trans.translate; + cubic.p3 = trans.mat.xy * cubic.p3.x + trans.mat.zw * cubic.p3.y + trans.translate; + } + + vec2 err_v = 3.0 * (cubic.p2 - cubic.p1) + cubic.p0 - cubic.p3; + float err = err_v.x * err_v.x + err_v.y * err_v.y; + // The number of quadratics. + uint n_quads = max(uint(ceil(pow(err * (1.0 / MAX_HYPOT2), 1.0 / 6.0))), 1); + n_quads = min(n_quads, MAX_QUADS); + SubdivResult keep_params[MAX_QUADS]; + // Iterate over quadratics and tote up the estimated number of segments. + float val = 0.0; + vec2 qp0 = cubic.p0; + float step = 1.0 / float(n_quads); + for (uint i = 0; i < n_quads; i++) { + float t = float(i + 1) * step; + vec2 qp2 = eval_cubic(cubic.p0, cubic.p1, cubic.p2, cubic.p3, t); + vec2 qp1 = eval_cubic(cubic.p0, cubic.p1, cubic.p2, cubic.p3, t - 0.5 * step); + qp1 = 2.0 * qp1 - 0.5 * (qp0 + qp2); + SubdivResult params = estimate_subdiv(qp0, qp1, qp2, sqrt(REM_ACCURACY)); + keep_params[i] = params; + val += params.val; + + qp0 = qp2; + } + uint n = max(uint(ceil(val * 0.5 / sqrt(REM_ACCURACY))), 1); + + bool is_stroke = fill_mode_from_flags(tag.flags) == MODE_STROKE; + uint path_ix = cubic.path_ix; + Path path = Path_read(conf.tile_alloc, PathRef(conf.tile_alloc.offset + path_ix * Path_size)); + Alloc path_alloc = new_alloc(path.tiles.offset, (path.bbox.z - path.bbox.x) * (path.bbox.w - path.bbox.y) * Tile_size, mem_ok); + ivec4 bbox = ivec4(path.bbox); + vec2 p0 = cubic.p0; + qp0 = cubic.p0; + float v_step = val / float(n); + int n_out = 1; + float val_sum = 0.0; + for (uint i = 0; i < n_quads; i++) { + float t = float(i + 1) * step; + vec2 qp2 = eval_cubic(cubic.p0, cubic.p1, cubic.p2, cubic.p3, t); + vec2 qp1 = eval_cubic(cubic.p0, cubic.p1, cubic.p2, cubic.p3, t - 0.5 * step); + qp1 = 2.0 * qp1 - 0.5 * (qp0 + qp2); + SubdivResult params = keep_params[i]; + float u0 = approx_parabola_inv_integral(params.a0); + float u2 = approx_parabola_inv_integral(params.a2); + float uscale = 1.0 / (u2 - u0); + float target = float(n_out) * v_step; + while (n_out == n || target < val_sum + params.val) { + vec2 p1; + if (n_out == n) { + p1 = cubic.p3; + } else { + float u = (target - val_sum) / params.val; + float a = mix(params.a0, params.a2, u); + float au = approx_parabola_inv_integral(a); + float t = (au - u0) * uscale; + p1 = eval_quad(qp0, qp1, qp2, t); + } + + // Output line segment + + // Bounding box of element in pixel coordinates. + float xmin = min(p0.x, p1.x) - cubic.stroke.x; + float xmax = max(p0.x, p1.x) + cubic.stroke.x; + float ymin = min(p0.y, p1.y) - cubic.stroke.y; + float ymax = max(p0.y, p1.y) + cubic.stroke.y; + float dx = p1.x - p0.x; + float dy = p1.y - p0.y; + // Set up for per-scanline coverage formula, below. + float invslope = abs(dy) < 1e-9 ? 1e9 : dx / dy; + float c = (cubic.stroke.x + abs(invslope) * (0.5 * float(TILE_HEIGHT_PX) + cubic.stroke.y)) * SX; + float b = invslope; // Note: assumes square tiles, otherwise scale. + float a = (p0.x - (p0.y - 0.5 * float(TILE_HEIGHT_PX)) * b) * SX; + + int x0 = int(floor(xmin * SX)); + int x1 = int(floor(xmax * SX) + 1); + int y0 = int(floor(ymin * SY)); + int y1 = int(floor(ymax * SY) + 1); + + x0 = clamp(x0, bbox.x, bbox.z); + y0 = clamp(y0, bbox.y, bbox.w); + x1 = clamp(x1, bbox.x, bbox.z); + y1 = clamp(y1, bbox.y, bbox.w); + float xc = a + b * float(y0); + int stride = bbox.z - bbox.x; + int base = (y0 - bbox.y) * stride - bbox.x; + // TODO: can be tighter, use c to bound width + uint n_tile_alloc = uint((x1 - x0) * (y1 - y0)); + // Consider using subgroups to aggregate atomic add. + MallocResult tile_alloc = malloc(n_tile_alloc * TileSeg_size); + if (tile_alloc.failed || !mem_ok) { + return; + } + uint tile_offset = tile_alloc.alloc.offset; + + TileSeg tile_seg; + + int xray = int(floor(p0.x*SX)); + int last_xray = int(floor(p1.x*SX)); + if (p0.y > p1.y) { + int tmp = xray; + xray = last_xray; + last_xray = tmp; + } + for (int y = y0; y < y1; y++) { + float tile_y0 = float(y * TILE_HEIGHT_PX); + int xbackdrop = max(xray + 1, bbox.x); + if (!is_stroke && min(p0.y, p1.y) < tile_y0 && xbackdrop < bbox.z) { + int backdrop = p1.y < p0.y ? 1 : -1; + TileRef tile_ref = Tile_index(path.tiles, uint(base + xbackdrop)); + uint tile_el = tile_ref.offset >> 2; + if (touch_mem(path_alloc, tile_el + 1)) { + atomicAdd(memory[tile_el + 1], backdrop); + } + } + + // next_xray is the xray for the next scanline; the line segment intersects + // all tiles between xray and next_xray. + int next_xray = last_xray; + if (y < y1 - 1) { + float tile_y1 = float((y + 1) * TILE_HEIGHT_PX); + float x_edge = mix(p0.x, p1.x, (tile_y1 - p0.y) / dy); + next_xray = int(floor(x_edge*SX)); + } + + int min_xray = min(xray, next_xray); + int max_xray = max(xray, next_xray); + int xx0 = min(int(floor(xc - c)), min_xray); + int xx1 = max(int(ceil(xc + c)), max_xray + 1); + xx0 = clamp(xx0, x0, x1); + xx1 = clamp(xx1, x0, x1); + + for (int x = xx0; x < xx1; x++) { + float tile_x0 = float(x * TILE_WIDTH_PX); + TileRef tile_ref = Tile_index(TileRef(path.tiles.offset), uint(base + x)); + uint tile_el = tile_ref.offset >> 2; + uint old = 0; + if (touch_mem(path_alloc, tile_el)) { + old = atomicExchange(memory[tile_el], tile_offset); + } + tile_seg.origin = p0; + tile_seg.vector = p1 - p0; + float y_edge = 0.0; + if (!is_stroke) { + y_edge = mix(p0.y, p1.y, (tile_x0 - p0.x) / dx); + if (min(p0.x, p1.x) < tile_x0) { + vec2 p = vec2(tile_x0, y_edge); + if (p0.x > p1.x) { + tile_seg.vector = p - p0; + } else { + tile_seg.origin = p; + tile_seg.vector = p1 - p; + } + // kernel4 uses sign(vector.x) for the sign of the intersection backdrop. + // Nudge zeroes towards the intended sign. + if (tile_seg.vector.x == 0) { + tile_seg.vector.x = sign(p1.x - p0.x)*1e-9; + } + } + if (x <= min_xray || max_xray < x) { + // Reject inconsistent intersections. + y_edge = 1e9; + } + } + tile_seg.y_edge = y_edge; + tile_seg.next.offset = old; + TileSeg_write(tile_alloc.alloc, TileSegRef(tile_offset), tile_seg); + tile_offset += TileSeg_size; + } + xc += b; + base += stride; + xray = next_xray; + } + + n_out += 1; + target += v_step; + p0 = p1; + } + val_sum += params.val; + + qp0 = qp2; + } + + break; + } +} diff --git a/vendor/gioui.org/shader/piet/path_coarse_abi.c b/vendor/gioui.org/shader/piet/path_coarse_abi.c new file mode 100644 index 0000000..575d260 --- /dev/null +++ b/vendor/gioui.org/shader/piet/path_coarse_abi.c @@ -0,0 +1,23 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +//go:build linux && (arm64 || arm || amd64) +// +build linux +// +build arm64 arm amd64 + +#include <stdint.h> +#include <stddef.h> +#include "abi.h" +#include "runtime.h" +#include "path_coarse_abi.h" + +const struct program_info path_coarse_program_info = { + .has_cbarriers = 0, + .min_memory_size = 100000, + .desc_set_size = sizeof(struct path_coarse_descriptor_set_layout), + .workgroup_size_x = 32, + .workgroup_size_y = 1, + .workgroup_size_z = 1, + .begin = path_coarse_coroutine_begin, + .await = path_coarse_coroutine_await, + .destroy = path_coarse_coroutine_destroy, +}; diff --git a/vendor/gioui.org/shader/piet/path_coarse_abi.go b/vendor/gioui.org/shader/piet/path_coarse_abi.go new file mode 100644 index 0000000..34d8ac3 --- /dev/null +++ b/vendor/gioui.org/shader/piet/path_coarse_abi.go @@ -0,0 +1,35 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +//go:build linux && (arm64 || arm || amd64) +// +build linux +// +build arm64 arm amd64 + +package piet + +import "gioui.org/cpu" +import "unsafe" + +/* +#cgo LDFLAGS: -lm + +#include <stdint.h> +#include <stdlib.h> +#include "abi.h" +#include "runtime.h" +#include "path_coarse_abi.h" +*/ +import "C" + +var Path_coarseProgramInfo = (*cpu.ProgramInfo)(unsafe.Pointer(&C.path_coarse_program_info)) + +type Path_coarseDescriptorSetLayout = C.struct_path_coarse_descriptor_set_layout + +const Path_coarseHash = "ed67e14c880cf92bdd7a9d520610e8c8b139907ff8b55df20464d353a7f58e79" + +func (l *Path_coarseDescriptorSetLayout) Binding0() *cpu.BufferDescriptor { + return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding0)) +} + +func (l *Path_coarseDescriptorSetLayout) Binding1() *cpu.BufferDescriptor { + return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding1)) +} diff --git a/vendor/gioui.org/shader/piet/path_coarse_abi.h b/vendor/gioui.org/shader/piet/path_coarse_abi.h new file mode 100644 index 0000000..9e43e8e --- /dev/null +++ b/vendor/gioui.org/shader/piet/path_coarse_abi.h @@ -0,0 +1,17 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +struct path_coarse_descriptor_set_layout { + struct buffer_descriptor binding0; + struct buffer_descriptor binding1; +}; + +extern coroutine path_coarse_coroutine_begin(struct program_data *data, + int32_t workgroupX, int32_t workgroupY, int32_t workgroupZ, + void *workgroupMemory, + int32_t firstSubgroup, + int32_t subgroupCount) ATTR_HIDDEN; + +extern bool path_coarse_coroutine_await(coroutine r, yield_result *res) ATTR_HIDDEN; +extern void path_coarse_coroutine_destroy(coroutine r) ATTR_HIDDEN; + +extern const struct program_info path_coarse_program_info ATTR_HIDDEN; diff --git a/vendor/gioui.org/shader/piet/path_coarse_abi_nosupport.go b/vendor/gioui.org/shader/piet/path_coarse_abi_nosupport.go new file mode 100644 index 0000000..f67a16a --- /dev/null +++ b/vendor/gioui.org/shader/piet/path_coarse_abi_nosupport.go @@ -0,0 +1,22 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +//go:build !(linux && (arm64 || arm || amd64)) +// +build !linux !arm64,!arm,!amd64 + +package piet + +import "gioui.org/cpu" + +var Path_coarseProgramInfo *cpu.ProgramInfo + +type Path_coarseDescriptorSetLayout struct{} + +const Path_coarseHash = "" + +func (l *Path_coarseDescriptorSetLayout) Binding0() *cpu.BufferDescriptor { + panic("unsupported") +} + +func (l *Path_coarseDescriptorSetLayout) Binding1() *cpu.BufferDescriptor { + panic("unsupported") +} diff --git a/vendor/gioui.org/shader/piet/path_coarse_linux_amd64.syso b/vendor/gioui.org/shader/piet/path_coarse_linux_amd64.syso Binary files differnew file mode 100644 index 0000000..a9f91d5 --- /dev/null +++ b/vendor/gioui.org/shader/piet/path_coarse_linux_amd64.syso diff --git a/vendor/gioui.org/shader/piet/path_coarse_linux_arm.syso b/vendor/gioui.org/shader/piet/path_coarse_linux_arm.syso Binary files differnew file mode 100644 index 0000000..2fcd6a0 --- /dev/null +++ b/vendor/gioui.org/shader/piet/path_coarse_linux_arm.syso diff --git a/vendor/gioui.org/shader/piet/path_coarse_linux_arm64.syso b/vendor/gioui.org/shader/piet/path_coarse_linux_arm64.syso Binary files differnew file mode 100644 index 0000000..95f731a --- /dev/null +++ b/vendor/gioui.org/shader/piet/path_coarse_linux_arm64.syso diff --git a/vendor/gioui.org/shader/piet/pathseg.h b/vendor/gioui.org/shader/piet/pathseg.h new file mode 100644 index 0000000..749771e --- /dev/null +++ b/vendor/gioui.org/shader/piet/pathseg.h @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense + +// Code auto-generated by piet-gpu-derive + +struct PathCubicRef { + uint offset; +}; + +struct PathSegRef { + uint offset; +}; + +struct PathCubic { + vec2 p0; + vec2 p1; + vec2 p2; + vec2 p3; + uint path_ix; + uint trans_ix; + vec2 stroke; +}; + +#define PathCubic_size 48 + +PathCubicRef PathCubic_index(PathCubicRef ref, uint index) { + return PathCubicRef(ref.offset + index * PathCubic_size); +} + +#define PathSeg_Nop 0 +#define PathSeg_Cubic 1 +#define PathSeg_size 52 + +PathSegRef PathSeg_index(PathSegRef ref, uint index) { + return PathSegRef(ref.offset + index * PathSeg_size); +} + +struct PathSegTag { + uint tag; + uint flags; +}; + +PathCubic PathCubic_read(Alloc a, PathCubicRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = read_mem(a, ix + 0); + uint raw1 = read_mem(a, ix + 1); + uint raw2 = read_mem(a, ix + 2); + uint raw3 = read_mem(a, ix + 3); + uint raw4 = read_mem(a, ix + 4); + uint raw5 = read_mem(a, ix + 5); + uint raw6 = read_mem(a, ix + 6); + uint raw7 = read_mem(a, ix + 7); + uint raw8 = read_mem(a, ix + 8); + uint raw9 = read_mem(a, ix + 9); + uint raw10 = read_mem(a, ix + 10); + uint raw11 = read_mem(a, ix + 11); + PathCubic s; + s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); + s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.p2 = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5)); + s.p3 = vec2(uintBitsToFloat(raw6), uintBitsToFloat(raw7)); + s.path_ix = raw8; + s.trans_ix = raw9; + s.stroke = vec2(uintBitsToFloat(raw10), uintBitsToFloat(raw11)); + return s; +} + +void PathCubic_write(Alloc a, PathCubicRef ref, PathCubic s) { + uint ix = ref.offset >> 2; + write_mem(a, ix + 0, floatBitsToUint(s.p0.x)); + write_mem(a, ix + 1, floatBitsToUint(s.p0.y)); + write_mem(a, ix + 2, floatBitsToUint(s.p1.x)); + write_mem(a, ix + 3, floatBitsToUint(s.p1.y)); + write_mem(a, ix + 4, floatBitsToUint(s.p2.x)); + write_mem(a, ix + 5, floatBitsToUint(s.p2.y)); + write_mem(a, ix + 6, floatBitsToUint(s.p3.x)); + write_mem(a, ix + 7, floatBitsToUint(s.p3.y)); + write_mem(a, ix + 8, s.path_ix); + write_mem(a, ix + 9, s.trans_ix); + write_mem(a, ix + 10, floatBitsToUint(s.stroke.x)); + write_mem(a, ix + 11, floatBitsToUint(s.stroke.y)); +} + +PathSegTag PathSeg_tag(Alloc a, PathSegRef ref) { + uint tag_and_flags = read_mem(a, ref.offset >> 2); + return PathSegTag(tag_and_flags & 0xffff, tag_and_flags >> 16); +} + +PathCubic PathSeg_Cubic_read(Alloc a, PathSegRef ref) { + return PathCubic_read(a, PathCubicRef(ref.offset + 4)); +} + +void PathSeg_Nop_write(Alloc a, PathSegRef ref) { + write_mem(a, ref.offset >> 2, PathSeg_Nop); +} + +void PathSeg_Cubic_write(Alloc a, PathSegRef ref, uint flags, PathCubic s) { + write_mem(a, ref.offset >> 2, (flags << 16) | PathSeg_Cubic); + PathCubic_write(a, PathCubicRef(ref.offset + 4), s); +} + diff --git a/vendor/gioui.org/shader/piet/ptcl.h b/vendor/gioui.org/shader/piet/ptcl.h new file mode 100644 index 0000000..6267fc5 --- /dev/null +++ b/vendor/gioui.org/shader/piet/ptcl.h @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense + +// Code auto-generated by piet-gpu-derive + +struct CmdStrokeRef { + uint offset; +}; + +struct CmdFillRef { + uint offset; +}; + +struct CmdColorRef { + uint offset; +}; + +struct CmdImageRef { + uint offset; +}; + +struct CmdAlphaRef { + uint offset; +}; + +struct CmdJumpRef { + uint offset; +}; + +struct CmdRef { + uint offset; +}; + +struct CmdStroke { + uint tile_ref; + float half_width; +}; + +#define CmdStroke_size 8 + +CmdStrokeRef CmdStroke_index(CmdStrokeRef ref, uint index) { + return CmdStrokeRef(ref.offset + index * CmdStroke_size); +} + +struct CmdFill { + uint tile_ref; + int backdrop; +}; + +#define CmdFill_size 8 + +CmdFillRef CmdFill_index(CmdFillRef ref, uint index) { + return CmdFillRef(ref.offset + index * CmdFill_size); +} + +struct CmdColor { + uint rgba_color; +}; + +#define CmdColor_size 4 + +CmdColorRef CmdColor_index(CmdColorRef ref, uint index) { + return CmdColorRef(ref.offset + index * CmdColor_size); +} + +struct CmdImage { + uint index; + ivec2 offset; +}; + +#define CmdImage_size 8 + +CmdImageRef CmdImage_index(CmdImageRef ref, uint index) { + return CmdImageRef(ref.offset + index * CmdImage_size); +} + +struct CmdAlpha { + float alpha; +}; + +#define CmdAlpha_size 4 + +CmdAlphaRef CmdAlpha_index(CmdAlphaRef ref, uint index) { + return CmdAlphaRef(ref.offset + index * CmdAlpha_size); +} + +struct CmdJump { + uint new_ref; +}; + +#define CmdJump_size 4 + +CmdJumpRef CmdJump_index(CmdJumpRef ref, uint index) { + return CmdJumpRef(ref.offset + index * CmdJump_size); +} + +#define Cmd_End 0 +#define Cmd_Fill 1 +#define Cmd_Stroke 2 +#define Cmd_Solid 3 +#define Cmd_Alpha 4 +#define Cmd_Color 5 +#define Cmd_Image 6 +#define Cmd_BeginClip 7 +#define Cmd_EndClip 8 +#define Cmd_Jump 9 +#define Cmd_size 12 + +CmdRef Cmd_index(CmdRef ref, uint index) { + return CmdRef(ref.offset + index * Cmd_size); +} + +struct CmdTag { + uint tag; + uint flags; +}; + +CmdStroke CmdStroke_read(Alloc a, CmdStrokeRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = read_mem(a, ix + 0); + uint raw1 = read_mem(a, ix + 1); + CmdStroke s; + s.tile_ref = raw0; + s.half_width = uintBitsToFloat(raw1); + return s; +} + +void CmdStroke_write(Alloc a, CmdStrokeRef ref, CmdStroke s) { + uint ix = ref.offset >> 2; + write_mem(a, ix + 0, s.tile_ref); + write_mem(a, ix + 1, floatBitsToUint(s.half_width)); +} + +CmdFill CmdFill_read(Alloc a, CmdFillRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = read_mem(a, ix + 0); + uint raw1 = read_mem(a, ix + 1); + CmdFill s; + s.tile_ref = raw0; + s.backdrop = int(raw1); + return s; +} + +void CmdFill_write(Alloc a, CmdFillRef ref, CmdFill s) { + uint ix = ref.offset >> 2; + write_mem(a, ix + 0, s.tile_ref); + write_mem(a, ix + 1, uint(s.backdrop)); +} + +CmdColor CmdColor_read(Alloc a, CmdColorRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = read_mem(a, ix + 0); + CmdColor s; + s.rgba_color = raw0; + return s; +} + +void CmdColor_write(Alloc a, CmdColorRef ref, CmdColor s) { + uint ix = ref.offset >> 2; + write_mem(a, ix + 0, s.rgba_color); +} + +CmdImage CmdImage_read(Alloc a, CmdImageRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = read_mem(a, ix + 0); + uint raw1 = read_mem(a, ix + 1); + CmdImage s; + s.index = raw0; + s.offset = ivec2(int(raw1 << 16) >> 16, int(raw1) >> 16); + return s; +} + +void CmdImage_write(Alloc a, CmdImageRef ref, CmdImage s) { + uint ix = ref.offset >> 2; + write_mem(a, ix + 0, s.index); + write_mem(a, ix + 1, (uint(s.offset.x) & 0xffff) | (uint(s.offset.y) << 16)); +} + +CmdAlpha CmdAlpha_read(Alloc a, CmdAlphaRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = read_mem(a, ix + 0); + CmdAlpha s; + s.alpha = uintBitsToFloat(raw0); + return s; +} + +void CmdAlpha_write(Alloc a, CmdAlphaRef ref, CmdAlpha s) { + uint ix = ref.offset >> 2; + write_mem(a, ix + 0, floatBitsToUint(s.alpha)); +} + +CmdJump CmdJump_read(Alloc a, CmdJumpRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = read_mem(a, ix + 0); + CmdJump s; + s.new_ref = raw0; + return s; +} + +void CmdJump_write(Alloc a, CmdJumpRef ref, CmdJump s) { + uint ix = ref.offset >> 2; + write_mem(a, ix + 0, s.new_ref); +} + +CmdTag Cmd_tag(Alloc a, CmdRef ref) { + uint tag_and_flags = read_mem(a, ref.offset >> 2); + return CmdTag(tag_and_flags & 0xffff, tag_and_flags >> 16); +} + +CmdFill Cmd_Fill_read(Alloc a, CmdRef ref) { + return CmdFill_read(a, CmdFillRef(ref.offset + 4)); +} + +CmdStroke Cmd_Stroke_read(Alloc a, CmdRef ref) { + return CmdStroke_read(a, CmdStrokeRef(ref.offset + 4)); +} + +CmdAlpha Cmd_Alpha_read(Alloc a, CmdRef ref) { + return CmdAlpha_read(a, CmdAlphaRef(ref.offset + 4)); +} + +CmdColor Cmd_Color_read(Alloc a, CmdRef ref) { + return CmdColor_read(a, CmdColorRef(ref.offset + 4)); +} + +CmdImage Cmd_Image_read(Alloc a, CmdRef ref) { + return CmdImage_read(a, CmdImageRef(ref.offset + 4)); +} + +CmdJump Cmd_Jump_read(Alloc a, CmdRef ref) { + return CmdJump_read(a, CmdJumpRef(ref.offset + 4)); +} + +void Cmd_End_write(Alloc a, CmdRef ref) { + write_mem(a, ref.offset >> 2, Cmd_End); +} + +void Cmd_Fill_write(Alloc a, CmdRef ref, CmdFill s) { + write_mem(a, ref.offset >> 2, Cmd_Fill); + CmdFill_write(a, CmdFillRef(ref.offset + 4), s); +} + +void Cmd_Stroke_write(Alloc a, CmdRef ref, CmdStroke s) { + write_mem(a, ref.offset >> 2, Cmd_Stroke); + CmdStroke_write(a, CmdStrokeRef(ref.offset + 4), s); +} + +void Cmd_Solid_write(Alloc a, CmdRef ref) { + write_mem(a, ref.offset >> 2, Cmd_Solid); +} + +void Cmd_Alpha_write(Alloc a, CmdRef ref, CmdAlpha s) { + write_mem(a, ref.offset >> 2, Cmd_Alpha); + CmdAlpha_write(a, CmdAlphaRef(ref.offset + 4), s); +} + +void Cmd_Color_write(Alloc a, CmdRef ref, CmdColor s) { + write_mem(a, ref.offset >> 2, Cmd_Color); + CmdColor_write(a, CmdColorRef(ref.offset + 4), s); +} + +void Cmd_Image_write(Alloc a, CmdRef ref, CmdImage s) { + write_mem(a, ref.offset >> 2, Cmd_Image); + CmdImage_write(a, CmdImageRef(ref.offset + 4), s); +} + +void Cmd_BeginClip_write(Alloc a, CmdRef ref) { + write_mem(a, ref.offset >> 2, Cmd_BeginClip); +} + +void Cmd_EndClip_write(Alloc a, CmdRef ref) { + write_mem(a, ref.offset >> 2, Cmd_EndClip); +} + +void Cmd_Jump_write(Alloc a, CmdRef ref, CmdJump s) { + write_mem(a, ref.offset >> 2, Cmd_Jump); + CmdJump_write(a, CmdJumpRef(ref.offset + 4), s); +} + diff --git a/vendor/gioui.org/shader/piet/runtime.h b/vendor/gioui.org/shader/piet/runtime.h new file mode 100644 index 0000000..cfae912 --- /dev/null +++ b/vendor/gioui.org/shader/piet/runtime.h @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +#define ATTR_HIDDEN __attribute__ ((visibility ("hidden"))) + +// program_info contains constant parameters for a program. +struct program_info { + // MinMemorySize is the minimum size of memory passed to dispatch. + size_t min_memory_size; + // has_cbarriers is 1 when the program contains control barriers. + bool has_cbarriers; + // desc_set_size is the size of the first descriptor set for the program. + size_t desc_set_size; + int workgroup_size_x; + int workgroup_size_y; + int workgroup_size_z; + // Program entrypoints. + routine_begin begin; + routine_await await; + routine_destroy destroy; +}; + +// dispatch_context contains the information a program dispatch. +struct dispatch_context; + +// thread_context contains the working memory of a batch. It may be +// reused, but not concurrently. +struct thread_context; + +extern struct buffer_descriptor alloc_buffer(size_t size) ATTR_HIDDEN; +extern struct image_descriptor alloc_image_rgba(int width, int height) ATTR_HIDDEN; + +extern struct dispatch_context *alloc_dispatch_context(void) ATTR_HIDDEN; + +extern void free_dispatch_context(struct dispatch_context *c) ATTR_HIDDEN; + +extern struct thread_context *alloc_thread_context(void) ATTR_HIDDEN; + +extern void free_thread_context(struct thread_context *c) ATTR_HIDDEN; + +// prepare_dispatch initializes ctx to run a dispatch of a program distributed +// among nthreads threads. +extern void prepare_dispatch(struct dispatch_context *ctx, int nthreads, struct program_info *info, uint8_t *desc_set, int ngroupx, int ngroupy, int ngroupz) ATTR_HIDDEN; + +// dispatch_batch executes a dispatch batch. +extern void dispatch_thread(struct dispatch_context *ctx, int thread_idx, struct thread_context *thread) ATTR_HIDDEN; diff --git a/vendor/gioui.org/shader/piet/scene.h b/vendor/gioui.org/shader/piet/scene.h new file mode 100644 index 0000000..38c2549 --- /dev/null +++ b/vendor/gioui.org/shader/piet/scene.h @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense + +// Code auto-generated by piet-gpu-derive + +struct LineSegRef { + uint offset; +}; + +struct QuadSegRef { + uint offset; +}; + +struct CubicSegRef { + uint offset; +}; + +struct FillColorRef { + uint offset; +}; + +struct FillImageRef { + uint offset; +}; + +struct SetLineWidthRef { + uint offset; +}; + +struct TransformRef { + uint offset; +}; + +struct ClipRef { + uint offset; +}; + +struct SetFillModeRef { + uint offset; +}; + +struct ElementRef { + uint offset; +}; + +struct LineSeg { + vec2 p0; + vec2 p1; +}; + +#define LineSeg_size 16 + +LineSegRef LineSeg_index(LineSegRef ref, uint index) { + return LineSegRef(ref.offset + index * LineSeg_size); +} + +struct QuadSeg { + vec2 p0; + vec2 p1; + vec2 p2; +}; + +#define QuadSeg_size 24 + +QuadSegRef QuadSeg_index(QuadSegRef ref, uint index) { + return QuadSegRef(ref.offset + index * QuadSeg_size); +} + +struct CubicSeg { + vec2 p0; + vec2 p1; + vec2 p2; + vec2 p3; +}; + +#define CubicSeg_size 32 + +CubicSegRef CubicSeg_index(CubicSegRef ref, uint index) { + return CubicSegRef(ref.offset + index * CubicSeg_size); +} + +struct FillColor { + uint rgba_color; +}; + +#define FillColor_size 4 + +FillColorRef FillColor_index(FillColorRef ref, uint index) { + return FillColorRef(ref.offset + index * FillColor_size); +} + +struct FillImage { + uint index; + ivec2 offset; +}; + +#define FillImage_size 8 + +FillImageRef FillImage_index(FillImageRef ref, uint index) { + return FillImageRef(ref.offset + index * FillImage_size); +} + +struct SetLineWidth { + float width; +}; + +#define SetLineWidth_size 4 + +SetLineWidthRef SetLineWidth_index(SetLineWidthRef ref, uint index) { + return SetLineWidthRef(ref.offset + index * SetLineWidth_size); +} + +struct Transform { + vec4 mat; + vec2 translate; +}; + +#define Transform_size 24 + +TransformRef Transform_index(TransformRef ref, uint index) { + return TransformRef(ref.offset + index * Transform_size); +} + +struct Clip { + vec4 bbox; +}; + +#define Clip_size 16 + +ClipRef Clip_index(ClipRef ref, uint index) { + return ClipRef(ref.offset + index * Clip_size); +} + +struct SetFillMode { + uint fill_mode; +}; + +#define SetFillMode_size 4 + +SetFillModeRef SetFillMode_index(SetFillModeRef ref, uint index) { + return SetFillModeRef(ref.offset + index * SetFillMode_size); +} + +#define Element_Nop 0 +#define Element_Line 1 +#define Element_Quad 2 +#define Element_Cubic 3 +#define Element_FillColor 4 +#define Element_SetLineWidth 5 +#define Element_Transform 6 +#define Element_BeginClip 7 +#define Element_EndClip 8 +#define Element_FillImage 9 +#define Element_SetFillMode 10 +#define Element_size 36 + +ElementRef Element_index(ElementRef ref, uint index) { + return ElementRef(ref.offset + index * Element_size); +} + +struct ElementTag { + uint tag; + uint flags; +}; + +LineSeg LineSeg_read(LineSegRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = scene[ix + 0]; + uint raw1 = scene[ix + 1]; + uint raw2 = scene[ix + 2]; + uint raw3 = scene[ix + 3]; + LineSeg s; + s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); + s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + return s; +} + +QuadSeg QuadSeg_read(QuadSegRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = scene[ix + 0]; + uint raw1 = scene[ix + 1]; + uint raw2 = scene[ix + 2]; + uint raw3 = scene[ix + 3]; + uint raw4 = scene[ix + 4]; + uint raw5 = scene[ix + 5]; + QuadSeg s; + s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); + s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.p2 = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5)); + return s; +} + +CubicSeg CubicSeg_read(CubicSegRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = scene[ix + 0]; + uint raw1 = scene[ix + 1]; + uint raw2 = scene[ix + 2]; + uint raw3 = scene[ix + 3]; + uint raw4 = scene[ix + 4]; + uint raw5 = scene[ix + 5]; + uint raw6 = scene[ix + 6]; + uint raw7 = scene[ix + 7]; + CubicSeg s; + s.p0 = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); + s.p1 = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.p2 = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5)); + s.p3 = vec2(uintBitsToFloat(raw6), uintBitsToFloat(raw7)); + return s; +} + +FillColor FillColor_read(FillColorRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = scene[ix + 0]; + FillColor s; + s.rgba_color = raw0; + return s; +} + +FillImage FillImage_read(FillImageRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = scene[ix + 0]; + uint raw1 = scene[ix + 1]; + FillImage s; + s.index = raw0; + s.offset = ivec2(int(raw1 << 16) >> 16, int(raw1) >> 16); + return s; +} + +SetLineWidth SetLineWidth_read(SetLineWidthRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = scene[ix + 0]; + SetLineWidth s; + s.width = uintBitsToFloat(raw0); + return s; +} + +Transform Transform_read(TransformRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = scene[ix + 0]; + uint raw1 = scene[ix + 1]; + uint raw2 = scene[ix + 2]; + uint raw3 = scene[ix + 3]; + uint raw4 = scene[ix + 4]; + uint raw5 = scene[ix + 5]; + Transform s; + s.mat = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.translate = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5)); + return s; +} + +Clip Clip_read(ClipRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = scene[ix + 0]; + uint raw1 = scene[ix + 1]; + uint raw2 = scene[ix + 2]; + uint raw3 = scene[ix + 3]; + Clip s; + s.bbox = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + return s; +} + +SetFillMode SetFillMode_read(SetFillModeRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = scene[ix + 0]; + SetFillMode s; + s.fill_mode = raw0; + return s; +} + +ElementTag Element_tag(ElementRef ref) { + uint tag_and_flags = scene[ref.offset >> 2]; + return ElementTag(tag_and_flags & 0xffff, tag_and_flags >> 16); +} + +LineSeg Element_Line_read(ElementRef ref) { + return LineSeg_read(LineSegRef(ref.offset + 4)); +} + +QuadSeg Element_Quad_read(ElementRef ref) { + return QuadSeg_read(QuadSegRef(ref.offset + 4)); +} + +CubicSeg Element_Cubic_read(ElementRef ref) { + return CubicSeg_read(CubicSegRef(ref.offset + 4)); +} + +FillColor Element_FillColor_read(ElementRef ref) { + return FillColor_read(FillColorRef(ref.offset + 4)); +} + +SetLineWidth Element_SetLineWidth_read(ElementRef ref) { + return SetLineWidth_read(SetLineWidthRef(ref.offset + 4)); +} + +Transform Element_Transform_read(ElementRef ref) { + return Transform_read(TransformRef(ref.offset + 4)); +} + +Clip Element_BeginClip_read(ElementRef ref) { + return Clip_read(ClipRef(ref.offset + 4)); +} + +Clip Element_EndClip_read(ElementRef ref) { + return Clip_read(ClipRef(ref.offset + 4)); +} + +FillImage Element_FillImage_read(ElementRef ref) { + return FillImage_read(FillImageRef(ref.offset + 4)); +} + +SetFillMode Element_SetFillMode_read(ElementRef ref) { + return SetFillMode_read(SetFillModeRef(ref.offset + 4)); +} + diff --git a/vendor/gioui.org/shader/piet/setup.h b/vendor/gioui.org/shader/piet/setup.h new file mode 100644 index 0000000..83b6d1d --- /dev/null +++ b/vendor/gioui.org/shader/piet/setup.h @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense + +// Various constants for the sizes of groups and tiles. + +// Much of this will be made dynamic in various ways, but for now it's easiest +// to hardcode and keep all in one place. + +// A LG_WG_FACTOR of n scales workgroup sizes by 2^n. Use 0 for a +// maximum workgroup size of 128, or 1 for a maximum size of 256. +#define LG_WG_FACTOR 0 +#define WG_FACTOR (1<<LG_WG_FACTOR) + +#define TILE_WIDTH_PX 32 +#define TILE_HEIGHT_PX 32 + +#define PTCL_INITIAL_ALLOC 1024 + +// These should probably be renamed and/or reworked. In the binning +// kernel, they represent the number of bins. Also, the workgroup size +// of that kernel is equal to the number of bins, but should probably +// be more flexible (it's 512 in the K&L paper). +#define N_TILE_X 16 +#define N_TILE_Y (8 * WG_FACTOR) +#define N_TILE (N_TILE_X * N_TILE_Y) +#define LG_N_TILE (7 + LG_WG_FACTOR) +#define N_SLICE (N_TILE / 32) + +struct Config { + uint n_elements; // paths + uint n_pathseg; + uint width_in_tiles; + uint height_in_tiles; + Alloc tile_alloc; + Alloc bin_alloc; + Alloc ptcl_alloc; + Alloc pathseg_alloc; + Alloc anno_alloc; + Alloc trans_alloc; +}; + +// Fill modes. +#define MODE_NONZERO 0 +#define MODE_STROKE 1 + +// Size of kernel4 clip state, in words. +#define CLIP_STATE_SIZE 2 + +// fill_mode_from_flags extracts the fill mode from tag flags. +uint fill_mode_from_flags(uint flags) { + return flags & 0x1; +} diff --git a/vendor/gioui.org/shader/piet/shaders.go b/vendor/gioui.org/shader/piet/shaders.go new file mode 100644 index 0000000..b4b6996 --- /dev/null +++ b/vendor/gioui.org/shader/piet/shaders.go @@ -0,0 +1,268 @@ +// Code generated by build.go. DO NOT EDIT. + +package piet + +import ( + _ "embed" + "runtime" + + "gioui.org/shader" +) + +var ( + Shader_backdrop_comp = shader.Sources{ + Name: "backdrop.comp", + StorageBuffers: []shader.BufferBinding{{Name: "Memory", Binding: 0}, {Name: "ConfigBuf", Binding: 1}}, + WorkgroupSize: [3]int{128, 1, 1}, + } + //go:embed zbackdrop.comp.0.spirv + zbackdrop_comp_0_spirv string + //go:embed zbackdrop.comp.0.dxbc + zbackdrop_comp_0_dxbc string + //go:embed zbackdrop.comp.0.metallibmacos + zbackdrop_comp_0_metallibmacos string + //go:embed zbackdrop.comp.0.metallibios + zbackdrop_comp_0_metallibios string + //go:embed zbackdrop.comp.0.metallibiossimulator + zbackdrop_comp_0_metallibiossimulator string + Shader_binning_comp = shader.Sources{ + Name: "binning.comp", + StorageBuffers: []shader.BufferBinding{{Name: "Memory", Binding: 0}, {Name: "ConfigBuf", Binding: 1}}, + WorkgroupSize: [3]int{128, 1, 1}, + } + //go:embed zbinning.comp.0.spirv + zbinning_comp_0_spirv string + //go:embed zbinning.comp.0.dxbc + zbinning_comp_0_dxbc string + //go:embed zbinning.comp.0.metallibmacos + zbinning_comp_0_metallibmacos string + //go:embed zbinning.comp.0.metallibios + zbinning_comp_0_metallibios string + //go:embed zbinning.comp.0.metallibiossimulator + zbinning_comp_0_metallibiossimulator string + Shader_coarse_comp = shader.Sources{ + Name: "coarse.comp", + StorageBuffers: []shader.BufferBinding{{Name: "Memory", Binding: 0}, {Name: "ConfigBuf", Binding: 1}}, + WorkgroupSize: [3]int{128, 1, 1}, + } + //go:embed zcoarse.comp.0.spirv + zcoarse_comp_0_spirv string + //go:embed zcoarse.comp.0.dxbc + zcoarse_comp_0_dxbc string + //go:embed zcoarse.comp.0.metallibmacos + zcoarse_comp_0_metallibmacos string + //go:embed zcoarse.comp.0.metallibios + zcoarse_comp_0_metallibios string + //go:embed zcoarse.comp.0.metallibiossimulator + zcoarse_comp_0_metallibiossimulator string + Shader_elements_comp = shader.Sources{ + Name: "elements.comp", + StorageBuffers: []shader.BufferBinding{{Name: "Memory", Binding: 0}, {Name: "SceneBuf", Binding: 2}, {Name: "StateBuf", Binding: 3}, {Name: "ConfigBuf", Binding: 1}}, + WorkgroupSize: [3]int{32, 1, 1}, + } + //go:embed zelements.comp.0.spirv + zelements_comp_0_spirv string + //go:embed zelements.comp.0.dxbc + zelements_comp_0_dxbc string + //go:embed zelements.comp.0.metallibmacos + zelements_comp_0_metallibmacos string + //go:embed zelements.comp.0.metallibios + zelements_comp_0_metallibios string + //go:embed zelements.comp.0.metallibiossimulator + zelements_comp_0_metallibiossimulator string + Shader_kernel4_comp = shader.Sources{ + Name: "kernel4.comp", + Images: []shader.ImageBinding{{Name: "images", Binding: 3}, {Name: "image", Binding: 2}}, + StorageBuffers: []shader.BufferBinding{{Name: "Memory", Binding: 0}, {Name: "ConfigBuf", Binding: 1}}, + WorkgroupSize: [3]int{16, 8, 1}, + } + //go:embed zkernel4.comp.0.spirv + zkernel4_comp_0_spirv string + //go:embed zkernel4.comp.0.dxbc + zkernel4_comp_0_dxbc string + //go:embed zkernel4.comp.0.metallibmacos + zkernel4_comp_0_metallibmacos string + //go:embed zkernel4.comp.0.metallibios + zkernel4_comp_0_metallibios string + //go:embed zkernel4.comp.0.metallibiossimulator + zkernel4_comp_0_metallibiossimulator string + Shader_path_coarse_comp = shader.Sources{ + Name: "path_coarse.comp", + StorageBuffers: []shader.BufferBinding{{Name: "Memory", Binding: 0}, {Name: "ConfigBuf", Binding: 1}}, + WorkgroupSize: [3]int{32, 1, 1}, + } + //go:embed zpath_coarse.comp.0.spirv + zpath_coarse_comp_0_spirv string + //go:embed zpath_coarse.comp.0.dxbc + zpath_coarse_comp_0_dxbc string + //go:embed zpath_coarse.comp.0.metallibmacos + zpath_coarse_comp_0_metallibmacos string + //go:embed zpath_coarse.comp.0.metallibios + zpath_coarse_comp_0_metallibios string + //go:embed zpath_coarse.comp.0.metallibiossimulator + zpath_coarse_comp_0_metallibiossimulator string + Shader_tile_alloc_comp = shader.Sources{ + Name: "tile_alloc.comp", + StorageBuffers: []shader.BufferBinding{{Name: "Memory", Binding: 0}, {Name: "ConfigBuf", Binding: 1}}, + WorkgroupSize: [3]int{128, 1, 1}, + } + //go:embed ztile_alloc.comp.0.spirv + ztile_alloc_comp_0_spirv string + //go:embed ztile_alloc.comp.0.dxbc + ztile_alloc_comp_0_dxbc string + //go:embed ztile_alloc.comp.0.metallibmacos + ztile_alloc_comp_0_metallibmacos string + //go:embed ztile_alloc.comp.0.metallibios + ztile_alloc_comp_0_metallibios string + //go:embed ztile_alloc.comp.0.metallibiossimulator + ztile_alloc_comp_0_metallibiossimulator string +) + +func init() { + const ( + opengles = runtime.GOOS == "linux" || runtime.GOOS == "freebsd" || runtime.GOOS == "openbsd" || runtime.GOOS == "windows" || runtime.GOOS == "js" || runtime.GOOS == "android" || runtime.GOOS == "darwin" || runtime.GOOS == "ios" + opengl = runtime.GOOS == "darwin" + d3d11 = runtime.GOOS == "windows" + vulkan = runtime.GOOS == "linux" || runtime.GOOS == "android" + ) + if vulkan { + Shader_backdrop_comp.SPIRV = zbackdrop_comp_0_spirv + } + if opengles { + } + if opengl { + } + if d3d11 { + Shader_backdrop_comp.DXBC = zbackdrop_comp_0_dxbc + } + if runtime.GOOS == "darwin" { + Shader_backdrop_comp.MetalLib = zbackdrop_comp_0_metallibmacos + } + if runtime.GOOS == "ios" { + if runtime.GOARCH == "amd64" { + Shader_backdrop_comp.MetalLib = zbackdrop_comp_0_metallibiossimulator + } else { + Shader_backdrop_comp.MetalLib = zbackdrop_comp_0_metallibios + } + } + if vulkan { + Shader_binning_comp.SPIRV = zbinning_comp_0_spirv + } + if opengles { + } + if opengl { + } + if d3d11 { + Shader_binning_comp.DXBC = zbinning_comp_0_dxbc + } + if runtime.GOOS == "darwin" { + Shader_binning_comp.MetalLib = zbinning_comp_0_metallibmacos + } + if runtime.GOOS == "ios" { + if runtime.GOARCH == "amd64" { + Shader_binning_comp.MetalLib = zbinning_comp_0_metallibiossimulator + } else { + Shader_binning_comp.MetalLib = zbinning_comp_0_metallibios + } + } + if vulkan { + Shader_coarse_comp.SPIRV = zcoarse_comp_0_spirv + } + if opengles { + } + if opengl { + } + if d3d11 { + Shader_coarse_comp.DXBC = zcoarse_comp_0_dxbc + } + if runtime.GOOS == "darwin" { + Shader_coarse_comp.MetalLib = zcoarse_comp_0_metallibmacos + } + if runtime.GOOS == "ios" { + if runtime.GOARCH == "amd64" { + Shader_coarse_comp.MetalLib = zcoarse_comp_0_metallibiossimulator + } else { + Shader_coarse_comp.MetalLib = zcoarse_comp_0_metallibios + } + } + if vulkan { + Shader_elements_comp.SPIRV = zelements_comp_0_spirv + } + if opengles { + } + if opengl { + } + if d3d11 { + Shader_elements_comp.DXBC = zelements_comp_0_dxbc + } + if runtime.GOOS == "darwin" { + Shader_elements_comp.MetalLib = zelements_comp_0_metallibmacos + } + if runtime.GOOS == "ios" { + if runtime.GOARCH == "amd64" { + Shader_elements_comp.MetalLib = zelements_comp_0_metallibiossimulator + } else { + Shader_elements_comp.MetalLib = zelements_comp_0_metallibios + } + } + if vulkan { + Shader_kernel4_comp.SPIRV = zkernel4_comp_0_spirv + } + if opengles { + } + if opengl { + } + if d3d11 { + Shader_kernel4_comp.DXBC = zkernel4_comp_0_dxbc + } + if runtime.GOOS == "darwin" { + Shader_kernel4_comp.MetalLib = zkernel4_comp_0_metallibmacos + } + if runtime.GOOS == "ios" { + if runtime.GOARCH == "amd64" { + Shader_kernel4_comp.MetalLib = zkernel4_comp_0_metallibiossimulator + } else { + Shader_kernel4_comp.MetalLib = zkernel4_comp_0_metallibios + } + } + if vulkan { + Shader_path_coarse_comp.SPIRV = zpath_coarse_comp_0_spirv + } + if opengles { + } + if opengl { + } + if d3d11 { + Shader_path_coarse_comp.DXBC = zpath_coarse_comp_0_dxbc + } + if runtime.GOOS == "darwin" { + Shader_path_coarse_comp.MetalLib = zpath_coarse_comp_0_metallibmacos + } + if runtime.GOOS == "ios" { + if runtime.GOARCH == "amd64" { + Shader_path_coarse_comp.MetalLib = zpath_coarse_comp_0_metallibiossimulator + } else { + Shader_path_coarse_comp.MetalLib = zpath_coarse_comp_0_metallibios + } + } + if vulkan { + Shader_tile_alloc_comp.SPIRV = ztile_alloc_comp_0_spirv + } + if opengles { + } + if opengl { + } + if d3d11 { + Shader_tile_alloc_comp.DXBC = ztile_alloc_comp_0_dxbc + } + if runtime.GOOS == "darwin" { + Shader_tile_alloc_comp.MetalLib = ztile_alloc_comp_0_metallibmacos + } + if runtime.GOOS == "ios" { + if runtime.GOARCH == "amd64" { + Shader_tile_alloc_comp.MetalLib = ztile_alloc_comp_0_metallibiossimulator + } else { + Shader_tile_alloc_comp.MetalLib = ztile_alloc_comp_0_metallibios + } + } +} diff --git a/vendor/gioui.org/shader/piet/state.h b/vendor/gioui.org/shader/piet/state.h new file mode 100644 index 0000000..d2df804 --- /dev/null +++ b/vendor/gioui.org/shader/piet/state.h @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense + +// Code auto-generated by piet-gpu-derive + +struct StateRef { + uint offset; +}; + +struct State { + vec4 mat; + vec2 translate; + vec4 bbox; + float linewidth; + uint flags; + uint path_count; + uint pathseg_count; + uint trans_count; +}; + +#define State_size 60 + +StateRef State_index(StateRef ref, uint index) { + return StateRef(ref.offset + index * State_size); +} + +State State_read(StateRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = state[ix + 0]; + uint raw1 = state[ix + 1]; + uint raw2 = state[ix + 2]; + uint raw3 = state[ix + 3]; + uint raw4 = state[ix + 4]; + uint raw5 = state[ix + 5]; + uint raw6 = state[ix + 6]; + uint raw7 = state[ix + 7]; + uint raw8 = state[ix + 8]; + uint raw9 = state[ix + 9]; + uint raw10 = state[ix + 10]; + uint raw11 = state[ix + 11]; + uint raw12 = state[ix + 12]; + uint raw13 = state[ix + 13]; + uint raw14 = state[ix + 14]; + State s; + s.mat = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.translate = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5)); + s.bbox = vec4(uintBitsToFloat(raw6), uintBitsToFloat(raw7), uintBitsToFloat(raw8), uintBitsToFloat(raw9)); + s.linewidth = uintBitsToFloat(raw10); + s.flags = raw11; + s.path_count = raw12; + s.pathseg_count = raw13; + s.trans_count = raw14; + return s; +} + +void State_write(StateRef ref, State s) { + uint ix = ref.offset >> 2; + state[ix + 0] = floatBitsToUint(s.mat.x); + state[ix + 1] = floatBitsToUint(s.mat.y); + state[ix + 2] = floatBitsToUint(s.mat.z); + state[ix + 3] = floatBitsToUint(s.mat.w); + state[ix + 4] = floatBitsToUint(s.translate.x); + state[ix + 5] = floatBitsToUint(s.translate.y); + state[ix + 6] = floatBitsToUint(s.bbox.x); + state[ix + 7] = floatBitsToUint(s.bbox.y); + state[ix + 8] = floatBitsToUint(s.bbox.z); + state[ix + 9] = floatBitsToUint(s.bbox.w); + state[ix + 10] = floatBitsToUint(s.linewidth); + state[ix + 11] = s.flags; + state[ix + 12] = s.path_count; + state[ix + 13] = s.pathseg_count; + state[ix + 14] = s.trans_count; +} + diff --git a/vendor/gioui.org/shader/piet/support.c b/vendor/gioui.org/shader/piet/support.c new file mode 100644 index 0000000..1b7dd42 --- /dev/null +++ b/vendor/gioui.org/shader/piet/support.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build linux && (arm64 || arm || amd64) +// +build linux +// +build arm64 arm amd64 + +#include <stdint.h> +#include <stdlib.h> +#include <assert.h> +#include "abi.h" +#include "runtime.h" + +static void *malloc_align(size_t alignment, size_t size) { + void *ptr; + int ret = posix_memalign(&ptr, alignment, size); + assert(ret == 0); + return ptr; +} + +ATTR_HIDDEN void *coroutine_alloc_frame(size_t size) { + void *ptr = malloc_align(16, size); + return ptr; +} + +ATTR_HIDDEN void coroutine_free_frame(void *ptr) { + free(ptr); +} diff --git a/vendor/gioui.org/shader/piet/tile.h b/vendor/gioui.org/shader/piet/tile.h new file mode 100644 index 0000000..e11329c --- /dev/null +++ b/vendor/gioui.org/shader/piet/tile.h @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense + +// Code auto-generated by piet-gpu-derive + +struct PathRef { + uint offset; +}; + +struct TileRef { + uint offset; +}; + +struct TileSegRef { + uint offset; +}; + +struct TransformSegRef { + uint offset; +}; + +struct Path { + uvec4 bbox; + TileRef tiles; +}; + +#define Path_size 12 + +PathRef Path_index(PathRef ref, uint index) { + return PathRef(ref.offset + index * Path_size); +} + +struct Tile { + TileSegRef tile; + int backdrop; +}; + +#define Tile_size 8 + +TileRef Tile_index(TileRef ref, uint index) { + return TileRef(ref.offset + index * Tile_size); +} + +struct TileSeg { + vec2 origin; + vec2 vector; + float y_edge; + TileSegRef next; +}; + +#define TileSeg_size 24 + +TileSegRef TileSeg_index(TileSegRef ref, uint index) { + return TileSegRef(ref.offset + index * TileSeg_size); +} + +struct TransformSeg { + vec4 mat; + vec2 translate; +}; + +#define TransformSeg_size 24 + +TransformSegRef TransformSeg_index(TransformSegRef ref, uint index) { + return TransformSegRef(ref.offset + index * TransformSeg_size); +} + +Path Path_read(Alloc a, PathRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = read_mem(a, ix + 0); + uint raw1 = read_mem(a, ix + 1); + uint raw2 = read_mem(a, ix + 2); + Path s; + s.bbox = uvec4(raw0 & 0xffff, raw0 >> 16, raw1 & 0xffff, raw1 >> 16); + s.tiles = TileRef(raw2); + return s; +} + +void Path_write(Alloc a, PathRef ref, Path s) { + uint ix = ref.offset >> 2; + write_mem(a, ix + 0, s.bbox.x | (s.bbox.y << 16)); + write_mem(a, ix + 1, s.bbox.z | (s.bbox.w << 16)); + write_mem(a, ix + 2, s.tiles.offset); +} + +Tile Tile_read(Alloc a, TileRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = read_mem(a, ix + 0); + uint raw1 = read_mem(a, ix + 1); + Tile s; + s.tile = TileSegRef(raw0); + s.backdrop = int(raw1); + return s; +} + +void Tile_write(Alloc a, TileRef ref, Tile s) { + uint ix = ref.offset >> 2; + write_mem(a, ix + 0, s.tile.offset); + write_mem(a, ix + 1, uint(s.backdrop)); +} + +TileSeg TileSeg_read(Alloc a, TileSegRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = read_mem(a, ix + 0); + uint raw1 = read_mem(a, ix + 1); + uint raw2 = read_mem(a, ix + 2); + uint raw3 = read_mem(a, ix + 3); + uint raw4 = read_mem(a, ix + 4); + uint raw5 = read_mem(a, ix + 5); + TileSeg s; + s.origin = vec2(uintBitsToFloat(raw0), uintBitsToFloat(raw1)); + s.vector = vec2(uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.y_edge = uintBitsToFloat(raw4); + s.next = TileSegRef(raw5); + return s; +} + +void TileSeg_write(Alloc a, TileSegRef ref, TileSeg s) { + uint ix = ref.offset >> 2; + write_mem(a, ix + 0, floatBitsToUint(s.origin.x)); + write_mem(a, ix + 1, floatBitsToUint(s.origin.y)); + write_mem(a, ix + 2, floatBitsToUint(s.vector.x)); + write_mem(a, ix + 3, floatBitsToUint(s.vector.y)); + write_mem(a, ix + 4, floatBitsToUint(s.y_edge)); + write_mem(a, ix + 5, s.next.offset); +} + +TransformSeg TransformSeg_read(Alloc a, TransformSegRef ref) { + uint ix = ref.offset >> 2; + uint raw0 = read_mem(a, ix + 0); + uint raw1 = read_mem(a, ix + 1); + uint raw2 = read_mem(a, ix + 2); + uint raw3 = read_mem(a, ix + 3); + uint raw4 = read_mem(a, ix + 4); + uint raw5 = read_mem(a, ix + 5); + TransformSeg s; + s.mat = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3)); + s.translate = vec2(uintBitsToFloat(raw4), uintBitsToFloat(raw5)); + return s; +} + +void TransformSeg_write(Alloc a, TransformSegRef ref, TransformSeg s) { + uint ix = ref.offset >> 2; + write_mem(a, ix + 0, floatBitsToUint(s.mat.x)); + write_mem(a, ix + 1, floatBitsToUint(s.mat.y)); + write_mem(a, ix + 2, floatBitsToUint(s.mat.z)); + write_mem(a, ix + 3, floatBitsToUint(s.mat.w)); + write_mem(a, ix + 4, floatBitsToUint(s.translate.x)); + write_mem(a, ix + 5, floatBitsToUint(s.translate.y)); +} + diff --git a/vendor/gioui.org/shader/piet/tile_alloc.comp b/vendor/gioui.org/shader/piet/tile_alloc.comp new file mode 100644 index 0000000..0b6eca4 --- /dev/null +++ b/vendor/gioui.org/shader/piet/tile_alloc.comp @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense + +// Allocation and initialization of tiles for paths. + +#version 450 +#extension GL_GOOGLE_include_directive : enable + +#include "mem.h" +#include "setup.h" + +#define LG_TILE_ALLOC_WG (7 + LG_WG_FACTOR) +#define TILE_ALLOC_WG (1 << LG_TILE_ALLOC_WG) + +layout(local_size_x = TILE_ALLOC_WG, local_size_y = 1) in; + +layout(set = 0, binding = 1) readonly buffer ConfigBuf { + Config conf; +}; + +#include "annotated.h" +#include "tile.h" + +// scale factors useful for converting coordinates to tiles +#define SX (1.0 / float(TILE_WIDTH_PX)) +#define SY (1.0 / float(TILE_HEIGHT_PX)) + +shared uint sh_tile_count[TILE_ALLOC_WG]; +shared Alloc sh_tile_alloc; +// Really a bool, but some Metal devices don't accept shared bools. +shared uint sh_tile_alloc_failed; + +void main() { + uint th_ix = gl_LocalInvocationID.x; + uint element_ix = gl_GlobalInvocationID.x; + PathRef path_ref = PathRef(conf.tile_alloc.offset + element_ix * Path_size); + AnnotatedRef ref = AnnotatedRef(conf.anno_alloc.offset + element_ix * Annotated_size); + + uint tag = Annotated_Nop; + if (element_ix < conf.n_elements) { + tag = Annotated_tag(conf.anno_alloc, ref).tag; + } + int x0 = 0, y0 = 0, x1 = 0, y1 = 0; + switch (tag) { + case Annotated_Color: + case Annotated_Image: + case Annotated_BeginClip: + case Annotated_EndClip: + // Note: we take advantage of the fact that fills, strokes, and + // clips have compatible layout. + AnnoEndClip clip = Annotated_EndClip_read(conf.anno_alloc, ref); + x0 = int(floor(clip.bbox.x * SX)); + y0 = int(floor(clip.bbox.y * SY)); + x1 = int(ceil(clip.bbox.z * SX)); + y1 = int(ceil(clip.bbox.w * SY)); + break; + } + x0 = clamp(x0, 0, int(conf.width_in_tiles)); + y0 = clamp(y0, 0, int(conf.height_in_tiles)); + x1 = clamp(x1, 0, int(conf.width_in_tiles)); + y1 = clamp(y1, 0, int(conf.height_in_tiles)); + + Path path; + path.bbox = uvec4(x0, y0, x1, y1); + uint tile_count = (x1 - x0) * (y1 - y0); + if (tag == Annotated_EndClip) { + // Don't actually allocate tiles for an end clip, but we do want + // the path structure (especially bbox) allocated for it. + tile_count = 0; + } + + sh_tile_count[th_ix] = tile_count; + uint total_tile_count = tile_count; + // Prefix sum of sh_tile_count + for (uint i = 0; i < LG_TILE_ALLOC_WG; i++) { + barrier(); + if (th_ix >= (1 << i)) { + total_tile_count += sh_tile_count[th_ix - (1 << i)]; + } + barrier(); + sh_tile_count[th_ix] = total_tile_count; + } + if (th_ix == TILE_ALLOC_WG - 1) { + MallocResult res = malloc(total_tile_count * Tile_size); + sh_tile_alloc = res.alloc; + sh_tile_alloc_failed = res.failed ? 1 : 0; + } + barrier(); + if (sh_tile_alloc_failed != 0 || mem_error != NO_ERROR) { + return; + } + Alloc alloc_start = sh_tile_alloc; + + if (element_ix < conf.n_elements) { + uint tile_subix = th_ix > 0 ? sh_tile_count[th_ix - 1] : 0; + Alloc tiles_alloc = slice_mem(alloc_start, Tile_size * tile_subix, Tile_size * tile_count); + path.tiles = TileRef(tiles_alloc.offset); + Path_write(conf.tile_alloc, path_ref, path); + } + + // Zero out allocated tiles efficiently + uint total_count = sh_tile_count[TILE_ALLOC_WG - 1] * (Tile_size / 4); + uint start_ix = alloc_start.offset >> 2; + for (uint i = th_ix; i < total_count; i += TILE_ALLOC_WG) { + // Note: this interleaving is faster than using Tile_write + // by a significant amount. + write_mem(alloc_start, start_ix + i, 0); + } +} diff --git a/vendor/gioui.org/shader/piet/tile_alloc_abi.c b/vendor/gioui.org/shader/piet/tile_alloc_abi.c new file mode 100644 index 0000000..0cffe2c --- /dev/null +++ b/vendor/gioui.org/shader/piet/tile_alloc_abi.c @@ -0,0 +1,23 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +//go:build linux && (arm64 || arm || amd64) +// +build linux +// +build arm64 arm amd64 + +#include <stdint.h> +#include <stddef.h> +#include "abi.h" +#include "runtime.h" +#include "tile_alloc_abi.h" + +const struct program_info tile_alloc_program_info = { + .has_cbarriers = 1, + .min_memory_size = 100000, + .desc_set_size = sizeof(struct tile_alloc_descriptor_set_layout), + .workgroup_size_x = 128, + .workgroup_size_y = 1, + .workgroup_size_z = 1, + .begin = tile_alloc_coroutine_begin, + .await = tile_alloc_coroutine_await, + .destroy = tile_alloc_coroutine_destroy, +}; diff --git a/vendor/gioui.org/shader/piet/tile_alloc_abi.go b/vendor/gioui.org/shader/piet/tile_alloc_abi.go new file mode 100644 index 0000000..8d0414e --- /dev/null +++ b/vendor/gioui.org/shader/piet/tile_alloc_abi.go @@ -0,0 +1,35 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +//go:build linux && (arm64 || arm || amd64) +// +build linux +// +build arm64 arm amd64 + +package piet + +import "gioui.org/cpu" +import "unsafe" + +/* +#cgo LDFLAGS: -lm + +#include <stdint.h> +#include <stdlib.h> +#include "abi.h" +#include "runtime.h" +#include "tile_alloc_abi.h" +*/ +import "C" + +var Tile_allocProgramInfo = (*cpu.ProgramInfo)(unsafe.Pointer(&C.tile_alloc_program_info)) + +type Tile_allocDescriptorSetLayout = C.struct_tile_alloc_descriptor_set_layout + +const Tile_allocHash = "364b3cf559d02a86c751292bedc571d5ceef2df899de39ad483b4176294e9857" + +func (l *Tile_allocDescriptorSetLayout) Binding0() *cpu.BufferDescriptor { + return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding0)) +} + +func (l *Tile_allocDescriptorSetLayout) Binding1() *cpu.BufferDescriptor { + return (*cpu.BufferDescriptor)(unsafe.Pointer(&l.binding1)) +} diff --git a/vendor/gioui.org/shader/piet/tile_alloc_abi.h b/vendor/gioui.org/shader/piet/tile_alloc_abi.h new file mode 100644 index 0000000..aac2706 --- /dev/null +++ b/vendor/gioui.org/shader/piet/tile_alloc_abi.h @@ -0,0 +1,17 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +struct tile_alloc_descriptor_set_layout { + struct buffer_descriptor binding0; + struct buffer_descriptor binding1; +}; + +extern coroutine tile_alloc_coroutine_begin(struct program_data *data, + int32_t workgroupX, int32_t workgroupY, int32_t workgroupZ, + void *workgroupMemory, + int32_t firstSubgroup, + int32_t subgroupCount) ATTR_HIDDEN; + +extern bool tile_alloc_coroutine_await(coroutine r, yield_result *res) ATTR_HIDDEN; +extern void tile_alloc_coroutine_destroy(coroutine r) ATTR_HIDDEN; + +extern const struct program_info tile_alloc_program_info ATTR_HIDDEN; diff --git a/vendor/gioui.org/shader/piet/tile_alloc_abi_nosupport.go b/vendor/gioui.org/shader/piet/tile_alloc_abi_nosupport.go new file mode 100644 index 0000000..fb3a023 --- /dev/null +++ b/vendor/gioui.org/shader/piet/tile_alloc_abi_nosupport.go @@ -0,0 +1,22 @@ +// Code generated by gioui.org/cpu/cmd/compile DO NOT EDIT. + +//go:build !(linux && (arm64 || arm || amd64)) +// +build !linux !arm64,!arm,!amd64 + +package piet + +import "gioui.org/cpu" + +var Tile_allocProgramInfo *cpu.ProgramInfo + +type Tile_allocDescriptorSetLayout struct{} + +const Tile_allocHash = "" + +func (l *Tile_allocDescriptorSetLayout) Binding0() *cpu.BufferDescriptor { + panic("unsupported") +} + +func (l *Tile_allocDescriptorSetLayout) Binding1() *cpu.BufferDescriptor { + panic("unsupported") +} diff --git a/vendor/gioui.org/shader/piet/tile_alloc_linux_amd64.syso b/vendor/gioui.org/shader/piet/tile_alloc_linux_amd64.syso Binary files differnew file mode 100644 index 0000000..0f37c87 --- /dev/null +++ b/vendor/gioui.org/shader/piet/tile_alloc_linux_amd64.syso diff --git a/vendor/gioui.org/shader/piet/tile_alloc_linux_arm.syso b/vendor/gioui.org/shader/piet/tile_alloc_linux_arm.syso Binary files differnew file mode 100644 index 0000000..ab62099 --- /dev/null +++ b/vendor/gioui.org/shader/piet/tile_alloc_linux_arm.syso diff --git a/vendor/gioui.org/shader/piet/tile_alloc_linux_arm64.syso b/vendor/gioui.org/shader/piet/tile_alloc_linux_arm64.syso Binary files differnew file mode 100644 index 0000000..0c968b0 --- /dev/null +++ b/vendor/gioui.org/shader/piet/tile_alloc_linux_arm64.syso diff --git a/vendor/gioui.org/shader/piet/zbackdrop.comp.0.dxbc b/vendor/gioui.org/shader/piet/zbackdrop.comp.0.dxbc Binary files differnew file mode 100644 index 0000000..a255faf --- /dev/null +++ b/vendor/gioui.org/shader/piet/zbackdrop.comp.0.dxbc diff --git a/vendor/gioui.org/shader/piet/zbackdrop.comp.0.metallibios b/vendor/gioui.org/shader/piet/zbackdrop.comp.0.metallibios Binary files differnew file mode 100644 index 0000000..349eb6e --- /dev/null +++ b/vendor/gioui.org/shader/piet/zbackdrop.comp.0.metallibios diff --git a/vendor/gioui.org/shader/piet/zbackdrop.comp.0.metallibiossimulator b/vendor/gioui.org/shader/piet/zbackdrop.comp.0.metallibiossimulator Binary files differnew file mode 100644 index 0000000..2d9f890 --- /dev/null +++ b/vendor/gioui.org/shader/piet/zbackdrop.comp.0.metallibiossimulator diff --git a/vendor/gioui.org/shader/piet/zbackdrop.comp.0.metallibmacos b/vendor/gioui.org/shader/piet/zbackdrop.comp.0.metallibmacos Binary files differnew file mode 100644 index 0000000..a72cd7b --- /dev/null +++ b/vendor/gioui.org/shader/piet/zbackdrop.comp.0.metallibmacos diff --git a/vendor/gioui.org/shader/piet/zbackdrop.comp.0.spirv b/vendor/gioui.org/shader/piet/zbackdrop.comp.0.spirv Binary files differnew file mode 100644 index 0000000..e3ffcfb --- /dev/null +++ b/vendor/gioui.org/shader/piet/zbackdrop.comp.0.spirv diff --git a/vendor/gioui.org/shader/piet/zbinning.comp.0.dxbc b/vendor/gioui.org/shader/piet/zbinning.comp.0.dxbc Binary files differnew file mode 100644 index 0000000..ba04622 --- /dev/null +++ b/vendor/gioui.org/shader/piet/zbinning.comp.0.dxbc diff --git a/vendor/gioui.org/shader/piet/zbinning.comp.0.metallibios b/vendor/gioui.org/shader/piet/zbinning.comp.0.metallibios Binary files differnew file mode 100644 index 0000000..862042e --- /dev/null +++ b/vendor/gioui.org/shader/piet/zbinning.comp.0.metallibios diff --git a/vendor/gioui.org/shader/piet/zbinning.comp.0.metallibiossimulator b/vendor/gioui.org/shader/piet/zbinning.comp.0.metallibiossimulator Binary files differnew file mode 100644 index 0000000..c1e8019 --- /dev/null +++ b/vendor/gioui.org/shader/piet/zbinning.comp.0.metallibiossimulator diff --git a/vendor/gioui.org/shader/piet/zbinning.comp.0.metallibmacos b/vendor/gioui.org/shader/piet/zbinning.comp.0.metallibmacos Binary files differnew file mode 100644 index 0000000..27508e1 --- /dev/null +++ b/vendor/gioui.org/shader/piet/zbinning.comp.0.metallibmacos diff --git a/vendor/gioui.org/shader/piet/zbinning.comp.0.spirv b/vendor/gioui.org/shader/piet/zbinning.comp.0.spirv Binary files differnew file mode 100644 index 0000000..7cfcb0c --- /dev/null +++ b/vendor/gioui.org/shader/piet/zbinning.comp.0.spirv diff --git a/vendor/gioui.org/shader/piet/zcoarse.comp.0.dxbc b/vendor/gioui.org/shader/piet/zcoarse.comp.0.dxbc Binary files differnew file mode 100644 index 0000000..94a3259 --- /dev/null +++ b/vendor/gioui.org/shader/piet/zcoarse.comp.0.dxbc diff --git a/vendor/gioui.org/shader/piet/zcoarse.comp.0.metallibios b/vendor/gioui.org/shader/piet/zcoarse.comp.0.metallibios Binary files differnew file mode 100644 index 0000000..29c8dbf --- /dev/null +++ b/vendor/gioui.org/shader/piet/zcoarse.comp.0.metallibios diff --git a/vendor/gioui.org/shader/piet/zcoarse.comp.0.metallibiossimulator b/vendor/gioui.org/shader/piet/zcoarse.comp.0.metallibiossimulator Binary files differnew file mode 100644 index 0000000..12b533d --- /dev/null +++ b/vendor/gioui.org/shader/piet/zcoarse.comp.0.metallibiossimulator diff --git a/vendor/gioui.org/shader/piet/zcoarse.comp.0.metallibmacos b/vendor/gioui.org/shader/piet/zcoarse.comp.0.metallibmacos Binary files differnew file mode 100644 index 0000000..c9a781b --- /dev/null +++ b/vendor/gioui.org/shader/piet/zcoarse.comp.0.metallibmacos diff --git a/vendor/gioui.org/shader/piet/zcoarse.comp.0.spirv b/vendor/gioui.org/shader/piet/zcoarse.comp.0.spirv Binary files differnew file mode 100644 index 0000000..23e652a --- /dev/null +++ b/vendor/gioui.org/shader/piet/zcoarse.comp.0.spirv diff --git a/vendor/gioui.org/shader/piet/zelements.comp.0.dxbc b/vendor/gioui.org/shader/piet/zelements.comp.0.dxbc Binary files differnew file mode 100644 index 0000000..e4916b7 --- /dev/null +++ b/vendor/gioui.org/shader/piet/zelements.comp.0.dxbc diff --git a/vendor/gioui.org/shader/piet/zelements.comp.0.metallibios b/vendor/gioui.org/shader/piet/zelements.comp.0.metallibios Binary files differnew file mode 100644 index 0000000..1d5097f --- /dev/null +++ b/vendor/gioui.org/shader/piet/zelements.comp.0.metallibios diff --git a/vendor/gioui.org/shader/piet/zelements.comp.0.metallibiossimulator b/vendor/gioui.org/shader/piet/zelements.comp.0.metallibiossimulator Binary files differnew file mode 100644 index 0000000..367e234 --- /dev/null +++ b/vendor/gioui.org/shader/piet/zelements.comp.0.metallibiossimulator diff --git a/vendor/gioui.org/shader/piet/zelements.comp.0.metallibmacos b/vendor/gioui.org/shader/piet/zelements.comp.0.metallibmacos Binary files differnew file mode 100644 index 0000000..b405013 --- /dev/null +++ b/vendor/gioui.org/shader/piet/zelements.comp.0.metallibmacos diff --git a/vendor/gioui.org/shader/piet/zelements.comp.0.spirv b/vendor/gioui.org/shader/piet/zelements.comp.0.spirv Binary files differnew file mode 100644 index 0000000..84057ac --- /dev/null +++ b/vendor/gioui.org/shader/piet/zelements.comp.0.spirv diff --git a/vendor/gioui.org/shader/piet/zkernel4.comp.0.dxbc b/vendor/gioui.org/shader/piet/zkernel4.comp.0.dxbc Binary files differnew file mode 100644 index 0000000..f459a5d --- /dev/null +++ b/vendor/gioui.org/shader/piet/zkernel4.comp.0.dxbc diff --git a/vendor/gioui.org/shader/piet/zkernel4.comp.0.metallibios b/vendor/gioui.org/shader/piet/zkernel4.comp.0.metallibios Binary files differnew file mode 100644 index 0000000..a979d51 --- /dev/null +++ b/vendor/gioui.org/shader/piet/zkernel4.comp.0.metallibios diff --git a/vendor/gioui.org/shader/piet/zkernel4.comp.0.metallibiossimulator b/vendor/gioui.org/shader/piet/zkernel4.comp.0.metallibiossimulator Binary files differnew file mode 100644 index 0000000..1dbb051 --- /dev/null +++ b/vendor/gioui.org/shader/piet/zkernel4.comp.0.metallibiossimulator diff --git a/vendor/gioui.org/shader/piet/zkernel4.comp.0.metallibmacos b/vendor/gioui.org/shader/piet/zkernel4.comp.0.metallibmacos Binary files differnew file mode 100644 index 0000000..99a823b --- /dev/null +++ b/vendor/gioui.org/shader/piet/zkernel4.comp.0.metallibmacos diff --git a/vendor/gioui.org/shader/piet/zkernel4.comp.0.spirv b/vendor/gioui.org/shader/piet/zkernel4.comp.0.spirv Binary files differnew file mode 100644 index 0000000..d558185 --- /dev/null +++ b/vendor/gioui.org/shader/piet/zkernel4.comp.0.spirv diff --git a/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.dxbc b/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.dxbc Binary files differnew file mode 100644 index 0000000..70eaf00 --- /dev/null +++ b/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.dxbc diff --git a/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.metallibios b/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.metallibios Binary files differnew file mode 100644 index 0000000..e6c2985 --- /dev/null +++ b/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.metallibios diff --git a/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.metallibiossimulator b/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.metallibiossimulator Binary files differnew file mode 100644 index 0000000..edde25c --- /dev/null +++ b/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.metallibiossimulator diff --git a/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.metallibmacos b/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.metallibmacos Binary files differnew file mode 100644 index 0000000..d1c5653 --- /dev/null +++ b/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.metallibmacos diff --git a/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.spirv b/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.spirv Binary files differnew file mode 100644 index 0000000..078ea61 --- /dev/null +++ b/vendor/gioui.org/shader/piet/zpath_coarse.comp.0.spirv diff --git a/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.dxbc b/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.dxbc Binary files differnew file mode 100644 index 0000000..309a77a --- /dev/null +++ b/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.dxbc diff --git a/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.metallibios b/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.metallibios Binary files differnew file mode 100644 index 0000000..0e165cf --- /dev/null +++ b/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.metallibios diff --git a/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.metallibiossimulator b/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.metallibiossimulator Binary files differnew file mode 100644 index 0000000..a95ef45 --- /dev/null +++ b/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.metallibiossimulator diff --git a/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.metallibmacos b/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.metallibmacos Binary files differnew file mode 100644 index 0000000..be6ab47 --- /dev/null +++ b/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.metallibmacos diff --git a/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.spirv b/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.spirv Binary files differnew file mode 100644 index 0000000..06f9b60 --- /dev/null +++ b/vendor/gioui.org/shader/piet/ztile_alloc.comp.0.spirv diff --git a/vendor/gioui.org/shader/shader.go b/vendor/gioui.org/shader/shader.go new file mode 100644 index 0000000..e1263c1 --- /dev/null +++ b/vendor/gioui.org/shader/shader.go @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package shader + +type Sources struct { + Name string + SPIRV string + GLSL100ES string + GLSL150 string + DXBC string + MetalLib string + Uniforms UniformsReflection + Inputs []InputLocation + Textures []TextureBinding + StorageBuffers []BufferBinding + Images []ImageBinding + WorkgroupSize [3]int +} + +type UniformsReflection struct { + Locations []UniformLocation + Size int +} + +type ImageBinding struct { + Name string + Binding int +} + +type BufferBinding struct { + Name string + Binding int +} + +type TextureBinding struct { + Name string + Binding int +} + +type UniformLocation struct { + Name string + Type DataType + Size int + Offset int +} + +type InputLocation struct { + // For GLSL. + Name string + Location int + // For HLSL. + Semantic string + SemanticIndex int + + Type DataType + Size int +} + +type DataType uint8 + +const ( + DataTypeFloat DataType = iota + DataTypeInt + DataTypeShort +) diff --git a/vendor/gioui.org/text/lru.go b/vendor/gioui.org/text/lru.go index 85dbf9c..10448b1 100644 --- a/vendor/gioui.org/text/lru.go +++ b/vendor/gioui.org/text/lru.go @@ -3,7 +3,7 @@ package text import ( - "gioui.org/op" + "gioui.org/op/clip" "golang.org/x/image/math/fixed" ) @@ -26,7 +26,7 @@ type layoutElem struct { type path struct { next, prev *path key pathKey - val op.CallOp + val clip.PathSpec } type layoutKey struct { @@ -81,16 +81,16 @@ func (l *layoutCache) insert(lt *layoutElem) { lt.next.prev = lt } -func (c *pathCache) Get(k pathKey) (op.CallOp, bool) { +func (c *pathCache) Get(k pathKey) (clip.PathSpec, bool) { if v, ok := c.m[k]; ok { c.remove(v) c.insert(v) return v.val, true } - return op.CallOp{}, false + return clip.PathSpec{}, false } -func (c *pathCache) Put(k pathKey, v op.CallOp) { +func (c *pathCache) Put(k pathKey, v clip.PathSpec) { if c.m == nil { c.m = make(map[pathKey]*path) c.head = new(path) diff --git a/vendor/gioui.org/text/shaper.go b/vendor/gioui.org/text/shaper.go index c4ec48c..59020e0 100644 --- a/vendor/gioui.org/text/shaper.go +++ b/vendor/gioui.org/text/shaper.go @@ -6,21 +6,19 @@ import ( "io" "strings" - "gioui.org/op" "golang.org/x/image/math/fixed" + + "gioui.org/op/clip" ) // Shaper implements layout and shaping of text. type Shaper interface { // Layout a text according to a set of options. Layout(font Font, size fixed.Int26_6, maxWidth int, txt io.Reader) ([]Line, error) - // Shape a line of text and return a clipping operation for its outline. - Shape(font Font, size fixed.Int26_6, layout []Glyph) op.CallOp - - // LayoutString is like Layout, but for strings. + // LayoutString is Layout for strings. LayoutString(font Font, size fixed.Int26_6, maxWidth int, str string) []Line - // ShapeString is like Shape for lines previously laid out by LayoutString. - ShapeString(font Font, size fixed.Int26_6, str string, layout []Glyph) op.CallOp + // Shape a line of text and return a clipping operation for its outline. + Shape(font Font, size fixed.Int26_6, layout Layout) clip.PathSpec } // A FontFace is a Font and a matching Face. @@ -49,8 +47,7 @@ type faceCache struct { } func (c *Cache) lookup(font Font) *faceCache { - var f *faceCache - f = c.faceForStyle(font) + f := c.faceForStyle(font) if f == nil { font.Typeface = c.def f = c.faceForStyle(font) @@ -59,24 +56,42 @@ func (c *Cache) lookup(font Font) *faceCache { } func (c *Cache) faceForStyle(font Font) *faceCache { - tf := c.faces[font] - if tf == nil { - font := font - font.Weight = Normal - tf = c.faces[font] + if closest, ok := c.closestFont(font); ok { + return c.faces[closest] } - if tf == nil { - font := font - font.Style = Regular - tf = c.faces[font] + font.Style = Regular + if closest, ok := c.closestFont(font); ok { + return c.faces[closest] } - if tf == nil { - font := font - font.Style = Regular - font.Weight = Normal - tf = c.faces[font] + return nil +} + +// closestFont returns the closest Font by weight, in case of equality the +// lighter weight will be returned. +func (c *Cache) closestFont(lookup Font) (Font, bool) { + if c.faces[lookup] != nil { + return lookup, true } - return tf + found := false + var match Font + for cf := range c.faces { + if cf.Typeface != lookup.Typeface || cf.Variant != lookup.Variant || cf.Style != lookup.Style { + continue + } + if !found { + found = true + match = cf + continue + } + cDist := weightDistance(lookup.Weight, cf.Weight) + mDist := weightDistance(lookup.Weight, match.Weight) + if cDist < mDist { + match = cf + } else if cDist == mDist && cf.Weight < match.Weight { + match = cf + } + } + return match, found } func NewCache(collection []FontFace) *Cache { @@ -84,9 +99,6 @@ func NewCache(collection []FontFace) *Cache { faces: make(map[Font]*faceCache), } for i, ff := range collection { - if ff.Font.Weight == 0 { - ff.Font.Weight = Normal - } if i == 0 { c.def = ff.Font.Typeface } @@ -95,24 +107,23 @@ func NewCache(collection []FontFace) *Cache { return c } -func (s *Cache) Layout(font Font, size fixed.Int26_6, maxWidth int, txt io.Reader) ([]Line, error) { - cache := s.lookup(font) +// Layout implements the Shaper interface. +func (c *Cache) Layout(font Font, size fixed.Int26_6, maxWidth int, txt io.Reader) ([]Line, error) { + cache := c.lookup(font) return cache.face.Layout(size, maxWidth, txt) } -func (s *Cache) Shape(font Font, size fixed.Int26_6, layout []Glyph) op.CallOp { - cache := s.lookup(font) - return cache.face.Shape(size, layout) -} - -func (s *Cache) LayoutString(font Font, size fixed.Int26_6, maxWidth int, str string) []Line { - cache := s.lookup(font) +// LayoutString is a caching implementation of the Shaper interface. +func (c *Cache) LayoutString(font Font, size fixed.Int26_6, maxWidth int, str string) []Line { + cache := c.lookup(font) return cache.layout(size, maxWidth, str) } -func (s *Cache) ShapeString(font Font, size fixed.Int26_6, str string, layout []Glyph) op.CallOp { - cache := s.lookup(font) - return cache.shape(size, str, layout) +// Shape is a caching implementation of the Shaper interface. Shape assumes that the layout +// argument is unchanged from a call to Layout or LayoutString. +func (c *Cache) Shape(font Font, size fixed.Int26_6, layout Layout) clip.PathSpec { + cache := c.lookup(font) + return cache.shape(size, layout) } func (f *faceCache) layout(ppem fixed.Int26_6, maxWidth int, str string) []Line { @@ -132,13 +143,13 @@ func (f *faceCache) layout(ppem fixed.Int26_6, maxWidth int, str string) []Line return l } -func (f *faceCache) shape(ppem fixed.Int26_6, str string, layout []Glyph) op.CallOp { +func (f *faceCache) shape(ppem fixed.Int26_6, layout Layout) clip.PathSpec { if f == nil { - return op.CallOp{} + return clip.PathSpec{} } pk := pathKey{ ppem: ppem, - str: str, + str: layout.Text, } if clip, ok := f.pathCache.Get(pk); ok { return clip diff --git a/vendor/gioui.org/text/text.go b/vendor/gioui.org/text/text.go index 3ec7d1f..12d39f5 100644 --- a/vendor/gioui.org/text/text.go +++ b/vendor/gioui.org/text/text.go @@ -5,15 +5,13 @@ package text import ( "io" - "gioui.org/op" + "gioui.org/op/clip" "golang.org/x/image/math/fixed" ) // A Line contains the measurements of a line of text. type Line struct { - Layout []Glyph - // Len is the length in UTF8 bytes of the line. - Len int + Layout Layout // Width is the width of the line. Width fixed.Int26_6 // Ascent is the height above the baseline. @@ -25,15 +23,16 @@ type Line struct { Bounds fixed.Rectangle26_6 } -type Glyph struct { - Rune rune - Advance fixed.Int26_6 +type Layout struct { + Text string + Advances []fixed.Int26_6 } // Style is the font style. type Style int -// Weight is a font weight, in CSS units. +// Weight is a font weight, in CSS units subtracted 400 so the zero value +// is normal text weight. type Weight int // Font specify a particular typeface variant, style and weight. @@ -45,10 +44,11 @@ type Font struct { Weight Weight } -// Face implements text layout and shaping for a particular font. +// Face implements text layout and shaping for a particular font. All +// methods must be safe for concurrent use. type Face interface { Layout(ppem fixed.Int26_6, maxWidth int, txt io.Reader) ([]Line, error) - Shape(ppem fixed.Int26_6, str []Glyph) op.CallOp + Shape(ppem fixed.Int26_6, str Layout) clip.PathSpec } // Typeface identifies a particular typeface design. The empty @@ -72,9 +72,22 @@ const ( ) const ( - Normal Weight = 400 - Medium Weight = 500 - Bold Weight = 600 + Thin Weight = 100 - 400 + Hairline Weight = Thin + ExtraLight Weight = 200 - 400 + UltraLight Weight = ExtraLight + Light Weight = 300 - 400 + Normal Weight = 400 - 400 + Medium Weight = 500 - 400 + SemiBold Weight = 600 - 400 + DemiBold Weight = SemiBold + Bold Weight = 700 - 400 + ExtraBold Weight = 800 - 400 + UltraBold Weight = ExtraBold + Black Weight = 900 - 400 + Heavy Weight = Black + ExtraBlack Weight = 950 - 400 + UltraBlack Weight = ExtraBlack ) func (a Alignment) String() string { @@ -86,6 +99,56 @@ func (a Alignment) String() string { case Middle: return "Middle" default: - panic("unreachable") + panic("invalid Alignment") } } + +func (s Style) String() string { + switch s { + case Regular: + return "Regular" + case Italic: + return "Italic" + default: + panic("invalid Style") + } +} + +func (w Weight) String() string { + switch w { + case Thin: + return "Thin" + case ExtraLight: + return "ExtraLight" + case Light: + return "Light" + case Normal: + return "Normal" + case Medium: + return "Medium" + case SemiBold: + return "SemiBold" + case Bold: + return "Bold" + case ExtraBold: + return "ExtraBold" + case Black: + return "Black" + case ExtraBlack: + return "ExtraBlack" + default: + panic("invalid Weight") + } +} + +// weightDistance returns the distance value between two font weights. +func weightDistance(wa Weight, wb Weight) int { + // Avoid dealing with negative Weight values. + a := int(wa) + 400 + b := int(wb) + 400 + diff := a - b + if diff < 0 { + return -diff + } + return diff +} |