From 4647e9de53bbe1c54dc125fd6e7dfc6aa16baddc Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 13 Apr 2024 22:23:50 +0900 Subject: [PATCH 01/81] internal/glfw, internal/gamepad: update GLFW to v3.3.9 Updates #2960 --- internal/gamepad/gamepad_darwin.go | 11 +- internal/gamepad/gamepad_desktop_windows.go | 10 + internal/gamepad/gamepad_linux.go | 7 - internal/glfw/api_windows.go | 2 + internal/glfw/cocoa_init_darwin.m | 2 +- internal/glfw/cocoa_window_darwin.m | 15 +- internal/glfw/context_unix.c | 29 ++- internal/glfw/context_windows.go | 27 ++- internal/glfw/egl_context_unix.c | 100 +++++---- internal/glfw/glfw3_unix.h | 57 +++-- internal/glfw/glfw3native_unix.h | 4 +- internal/glfw/glx_context_linbsd.c | 4 + internal/glfw/glx_context_linbsd.h | 1 - internal/glfw/input_unix.c | 8 +- internal/glfw/input_windows.go | 3 + internal/glfw/nsgl_context_darwin.m | 9 +- internal/glfw/wgl_context_windows.go | 17 +- internal/glfw/win32_platform_windows.go | 2 + internal/glfw/win32_window_windows.go | 206 ++++++++++-------- internal/glfw/x11_window_linbsd.c | 101 +++++++-- .../opengl/gl/procaddr_linbsd.go | 3 + 21 files changed, 393 insertions(+), 225 deletions(-) diff --git a/internal/gamepad/gamepad_darwin.go b/internal/gamepad/gamepad_darwin.go index a571922b8..f3521c8b0 100644 --- a/internal/gamepad/gamepad_darwin.go +++ b/internal/gamepad/gamepad_darwin.go @@ -152,6 +152,14 @@ func (g *nativeGamepadsImpl) addDevice(device _IOHIDDeviceRef, gamepads *gamepad return } + elements := _IOHIDDeviceCopyMatchingElements(device, 0, kIOHIDOptionsTypeNone) + // It is reportedly possible for this to fail on macOS 13 Ventura + // if the application does not have input monitoring permissions + if elements == 0 { + return + } + defer _CFRelease(_CFTypeRef(elements)) + name := "Unknown" if prop := _IOHIDDeviceGetProperty(device, _CFStringCreateWithCString(kCFAllocatorDefault, kIOHIDProductKey, kCFStringEncodingUTF8)); prop != 0 { var cstr [256]byte @@ -189,9 +197,6 @@ func (g *nativeGamepadsImpl) addDevice(device _IOHIDDeviceRef, gamepads *gamepad bs[0], bs[1], bs[2], bs[3], bs[4], bs[5], bs[6], bs[7], bs[8], bs[9], bs[10], bs[11]) } - elements := _IOHIDDeviceCopyMatchingElements(device, 0, kIOHIDOptionsTypeNone) - defer _CFRelease(_CFTypeRef(elements)) - n := &nativeGamepadImpl{ device: device, } diff --git a/internal/gamepad/gamepad_desktop_windows.go b/internal/gamepad/gamepad_desktop_windows.go index 56f95cc82..4ee5e8d06 100644 --- a/internal/gamepad/gamepad_desktop_windows.go +++ b/internal/gamepad/gamepad_desktop_windows.go @@ -794,6 +794,16 @@ func (g *nativeGamepadDesktop) hatState(hat int) int { if g.xinputState.Gamepad.wButtons&_XINPUT_GAMEPAD_DPAD_LEFT != 0 { v |= hatLeft } + + // Treat invalid combinations as neither being pressed + // while preserving what data can be preserved + if (v&hatRight) != 0 && (v&hatLeft) != 0 { + v &^= hatRight | hatLeft + } + if (v&hatUp) != 0 && (v&hatDown) != 0 { + v &^= hatUp | hatDown + } + return v } diff --git a/internal/gamepad/gamepad_linux.go b/internal/gamepad/gamepad_linux.go index b9d5fb3f9..d97d43970 100644 --- a/internal/gamepad/gamepad_linux.go +++ b/internal/gamepad/gamepad_linux.go @@ -140,13 +140,6 @@ func (*nativeGamepadsImpl) openGamepad(gamepads *gamepads, path string) (err err return fmt.Errorf("gamepad: ioctl for an ID failed: %w", err) } - if !isBitSet(evBits, unix.EV_KEY) { - if err := unix.Close(fd); err != nil { - return err - } - - return nil - } if !isBitSet(evBits, unix.EV_ABS) { if err := unix.Close(fd); err != nil { return err diff --git a/internal/glfw/api_windows.go b/internal/glfw/api_windows.go index f114665bc..6255cd3fa 100644 --- a/internal/glfw/api_windows.go +++ b/internal/glfw/api_windows.go @@ -121,6 +121,7 @@ const ( _PFD_SUPPORT_OPENGL = 0x00000020 _PFD_TYPE_RGBA = 0 _QS_ALLEVENTS = _QS_INPUT | _QS_POSTMESSAGE | _QS_TIMER | _QS_PAINT | _QS_HOTKEY + _QS_ALLINPUT = _QS_INPUT | _QS_POSTMESSAGE | _QS_TIMER | _QS_PAINT | _QS_HOTKEY | _QS_SENDMESSAGE _QS_HOTKEY = 0x0080 _QS_INPUT = _QS_MOUSE | _QS_KEY | _QS_RAWINPUT _QS_KEY = 0x0001 @@ -130,6 +131,7 @@ const ( _QS_PAINT = 0x0020 _QS_POSTMESSAGE = 0x0008 _QS_RAWINPUT = 0x0400 + _QS_SENDMESSAGE = 0x0040 _QS_TIMER = 0x0010 _RID_INPUT = 0x10000003 _RIDEV_REMOVE = 0x00000001 diff --git a/internal/glfw/cocoa_init_darwin.m b/internal/glfw/cocoa_init_darwin.m index 177500d5c..a9780d165 100644 --- a/internal/glfw/cocoa_init_darwin.m +++ b/internal/glfw/cocoa_init_darwin.m @@ -227,7 +227,7 @@ static void createKeyTables(void) _glfw.ns.keycodes[0x6D] = GLFW_KEY_F10; _glfw.ns.keycodes[0x67] = GLFW_KEY_F11; _glfw.ns.keycodes[0x6F] = GLFW_KEY_F12; - _glfw.ns.keycodes[0x69] = GLFW_KEY_F13; + _glfw.ns.keycodes[0x69] = GLFW_KEY_PRINT_SCREEN; _glfw.ns.keycodes[0x6B] = GLFW_KEY_F14; _glfw.ns.keycodes[0x71] = GLFW_KEY_F15; _glfw.ns.keycodes[0x6A] = GLFW_KEY_F16; diff --git a/internal/glfw/cocoa_window_darwin.m b/internal/glfw/cocoa_window_darwin.m index 111a6d16b..1b1a7ebf1 100644 --- a/internal/glfw/cocoa_window_darwin.m +++ b/internal/glfw/cocoa_window_darwin.m @@ -285,10 +285,15 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; - (void)windowDidChangeOcclusionState:(NSNotification* )notification { - if ([window->ns.object occlusionState] & NSWindowOcclusionStateVisible) - window->ns.occluded = GLFW_FALSE; - else - window->ns.occluded = GLFW_TRUE; +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 + if ([window->ns.object respondsToSelector:@selector(occlusionState)]) + { + if ([window->ns.object occlusionState] & NSWindowOcclusionStateVisible) + window->ns.occluded = GLFW_FALSE; + else + window->ns.occluded = GLFW_TRUE; + } +#endif } @end @@ -1239,7 +1244,7 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, if (window->monitor) { - styleMask &= ~(NSWindowStyleMaskTitled | NSWindowStyleMaskClosable); + styleMask &= ~(NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable); styleMask |= NSWindowStyleMaskBorderless; } else diff --git a/internal/glfw/context_unix.c b/internal/glfw/context_unix.c index 3b01f86c4..58cba6951 100644 --- a/internal/glfw/context_unix.c +++ b/internal/glfw/context_unix.c @@ -26,16 +26,6 @@ // GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) { - if (ctxconfig->share) - { - if (ctxconfig->client == GLFW_NO_API || - ctxconfig->share->context.client == GLFW_NO_API) - { - _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); - return GLFW_FALSE; - } - } - if (ctxconfig->source != GLFW_NATIVE_CONTEXT_API && ctxconfig->source != GLFW_EGL_CONTEXT_API && ctxconfig->source != GLFW_OSMESA_CONTEXT_API) @@ -56,6 +46,23 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) return GLFW_FALSE; } + if (ctxconfig->share) + { + if (ctxconfig->client == GLFW_NO_API || + ctxconfig->share->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return GLFW_FALSE; + } + + if (ctxconfig->source != ctxconfig->share->context.source) + { + _glfwInputError(GLFW_INVALID_ENUM, + "Context creation APIs do not match between contexts"); + return GLFW_FALSE; + } + } + if (ctxconfig->client == GLFW_OPENGL_API) { if ((ctxconfig->major < 1 || ctxconfig->minor < 0) || @@ -334,6 +341,8 @@ GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window, previous = _glfwPlatformGetTls(&_glfw.contextSlot); glfwMakeContextCurrent((GLFWwindow*) window); + if (_glfwPlatformGetTls(&_glfw.contextSlot) != window) + return GLFW_FALSE; window->context.GetIntegerv = (PFNGLGETINTEGERVPROC) window->context.getProcAddress("glGetIntegerv"); diff --git a/internal/glfw/context_windows.go b/internal/glfw/context_windows.go index 2f7ea8d10..49e0fdc4c 100644 --- a/internal/glfw/context_windows.go +++ b/internal/glfw/context_windows.go @@ -17,12 +17,6 @@ import ( ) func checkValidContextConfig(ctxconfig *ctxconfig) error { - if ctxconfig.share != nil { - if ctxconfig.client == NoAPI || ctxconfig.share.context.client == NoAPI { - return NoWindowContext - } - } - if ctxconfig.source != NativeContextAPI && ctxconfig.source != EGLContextAPI && ctxconfig.source != OSMesaContextAPI { @@ -35,6 +29,15 @@ func checkValidContextConfig(ctxconfig *ctxconfig) error { return fmt.Errorf("glfw: invalid client API 0x%08X: %w", ctxconfig.client, InvalidEnum) } + if ctxconfig.share != nil { + if ctxconfig.client == NoAPI || ctxconfig.share.context.client == NoAPI { + return NoWindowContext + } + if ctxconfig.source != ctxconfig.share.context.source { + return fmt.Errorf("glfw: context creation APIs do not match between contexts: %w", InvalidEnum) + } + } + if ctxconfig.client == OpenGLAPI { if (ctxconfig.major < 1 || ctxconfig.minor < 0) || (ctxconfig.major == 1 && ctxconfig.minor > 5) || @@ -249,11 +252,11 @@ func (w *Window) refreshContextAttribs(ctxconfig *ctxconfig) (ferr error) { w.context.source = ctxconfig.source w.context.client = OpenGLAPI - p, err := _glfw.contextSlot.get() + p1, err := _glfw.contextSlot.get() if err != nil { return err } - previous := (*Window)(unsafe.Pointer(p)) + previous := (*Window)(unsafe.Pointer(p1)) defer func() { err := previous.MakeContextCurrent() if ferr == nil { @@ -264,6 +267,14 @@ func (w *Window) refreshContextAttribs(ctxconfig *ctxconfig) (ferr error) { return err } + p2, err := _glfw.contextSlot.get() + if err != nil { + return err + } + if (*Window)(unsafe.Pointer(p2)) != w { + return nil + } + getIntegerv := w.context.getProcAddress("glGetIntegerv") getString := w.context.getProcAddress("glGetString") if getIntegerv == 0 || getString == 0 { diff --git a/internal/glfw/egl_context_unix.c b/internal/glfw/egl_context_unix.c index f415aef90..ba1fd9804 100644 --- a/internal/glfw/egl_context_unix.c +++ b/internal/glfw/egl_context_unix.c @@ -66,13 +66,30 @@ static int getEGLConfigAttrib(EGLConfig config, int attrib) // Return the EGLConfig most closely matching the specified hints // static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, - const _GLFWfbconfig* desired, + const _GLFWfbconfig* fbconfig, EGLConfig* result) { EGLConfig* nativeConfigs; _GLFWfbconfig* usableConfigs; const _GLFWfbconfig* closest; - int i, nativeCount, usableCount; + int i, nativeCount, usableCount, apiBit; + GLFWbool wrongApiAvailable = GLFW_FALSE; + + if (ctxconfig->client == GLFW_OPENGL_ES_API) + { + if (ctxconfig->major == 1) + apiBit = EGL_OPENGL_ES_BIT; + else + apiBit = EGL_OPENGL_ES2_BIT; + } + else + apiBit = EGL_OPENGL_BIT; + + if (fbconfig->stereo) + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "EGL: Stereo rendering not supported"); + return GLFW_FALSE; + } eglGetConfigs(_glfw.egl.display, NULL, 0, &nativeCount); if (!nativeCount) @@ -109,7 +126,7 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, if (!vi.visualid) continue; - if (desired->transparent) + if (fbconfig->transparent) { int count; XVisualInfo* vis = @@ -123,23 +140,10 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, } #endif // _GLFW_X11 - if (ctxconfig->client == GLFW_OPENGL_ES_API) + if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & apiBit)) { - if (ctxconfig->major == 1) - { - if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES_BIT)) - continue; - } - else - { - if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES2_BIT)) - continue; - } - } - else if (ctxconfig->client == GLFW_OPENGL_API) - { - if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_BIT)) - continue; + wrongApiAvailable = GLFW_TRUE; + continue; } u->redBits = getEGLConfigAttrib(n, EGL_RED_SIZE); @@ -151,15 +155,44 @@ static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, u->stencilBits = getEGLConfigAttrib(n, EGL_STENCIL_SIZE); u->samples = getEGLConfigAttrib(n, EGL_SAMPLES); - u->doublebuffer = desired->doublebuffer; + u->doublebuffer = fbconfig->doublebuffer; u->handle = (uintptr_t) n; usableCount++; } - closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount); + closest = _glfwChooseFBConfig(fbconfig, usableConfigs, usableCount); if (closest) *result = (EGLConfig) closest->handle; + else + { + if (wrongApiAvailable) + { + if (ctxconfig->client == GLFW_OPENGL_ES_API) + { + if (ctxconfig->major == 1) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "EGL: Failed to find support for OpenGL ES 1.x"); + } + else + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "EGL: Failed to find support for OpenGL ES 2 or later"); + } + } + else + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "EGL: Failed to find support for OpenGL"); + } + } + else + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "EGL: Failed to find a suitable EGLConfig"); + } + } free(nativeConfigs); free(usableConfigs); @@ -231,6 +264,7 @@ static int extensionSupportedEGL(const char* extension) static GLFWglproc getProcAddressEGL(const char* procname) { _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); + assert(window != NULL); if (window->context.egl.client) { @@ -454,11 +488,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, share = ctxconfig->share->context.egl.handle; if (!chooseEGLConfig(ctxconfig, fbconfig, &config)) - { - _glfwInputError(GLFW_FORMAT_UNAVAILABLE, - "EGL: Failed to find a suitable EGLConfig"); return GLFW_FALSE; - } if (ctxconfig->client == GLFW_OPENGL_ES_API) { @@ -515,18 +545,18 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, flags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR; } - if (ctxconfig->noerror) - { - if (_glfw.egl.KHR_create_context_no_error) - setAttrib(EGL_CONTEXT_OPENGL_NO_ERROR_KHR, GLFW_TRUE); - } - if (ctxconfig->major != 1 || ctxconfig->minor != 0) { setAttrib(EGL_CONTEXT_MAJOR_VERSION_KHR, ctxconfig->major); setAttrib(EGL_CONTEXT_MINOR_VERSION_KHR, ctxconfig->minor); } + if (ctxconfig->noerror) + { + if (_glfw.egl.KHR_create_context_no_error) + setAttrib(EGL_CONTEXT_OPENGL_NO_ERROR_KHR, GLFW_TRUE); + } + if (mask) setAttrib(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, mask); @@ -578,9 +608,6 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, if (!fbconfig->doublebuffer) setAttrib(EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER); - if (_glfw.egl.EXT_present_opaque) - setAttrib(EGL_PRESENT_OPAQUE_EXT, !fbconfig->transparent); - setAttrib(EGL_NONE, EGL_NONE); window->context.egl.surface = @@ -640,6 +667,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, #elif defined(__OpenBSD__) || defined(__NetBSD__) "libGL.so", #else + "libOpenGL.so.0", "libGL.so.1", #endif NULL @@ -702,11 +730,7 @@ GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig, const long vimask = VisualScreenMask | VisualIDMask; if (!chooseEGLConfig(ctxconfig, fbconfig, &native)) - { - _glfwInputError(GLFW_FORMAT_UNAVAILABLE, - "EGL: Failed to find a suitable EGLConfig"); return GLFW_FALSE; - } eglGetConfigAttrib(_glfw.egl.display, native, EGL_NATIVE_VISUAL_ID, &visualID); diff --git a/internal/glfw/glfw3_unix.h b/internal/glfw/glfw3_unix.h index 2a7cff514..9070de920 100644 --- a/internal/glfw/glfw3_unix.h +++ b/internal/glfw/glfw3_unix.h @@ -249,7 +249,7 @@ extern "C" { * release is made that does not contain any API changes. * @ingroup init */ -#define GLFW_VERSION_REVISION 8 +#define GLFW_VERSION_REVISION 9 /*! @} */ /*! @brief One. @@ -296,8 +296,12 @@ extern "C" { #define GLFW_REPEAT 2 /*! @} */ -/*! @defgroup keys Keyboard keys - * @brief Keyboard key IDs. +/*! @ingroup input + */ +#define GLFW_KEY_UNKNOWN -1 + +/*! @defgroup keys Keyboard key tokens + * @brief Keyboard key tokens. * * See [key input](@ref input_key) for how these are used. * @@ -320,8 +324,6 @@ extern "C" { * @{ */ -/* The unknown key */ -#define GLFW_KEY_UNKNOWN -1 /* Printable keys */ #define GLFW_KEY_SPACE 32 @@ -4039,8 +4041,8 @@ GLFWAPI int glfwRawMouseMotionSupported(void); * @param[in] scancode The scancode of the key to query. * @return The UTF-8 encoded, layout-specific name of the key, or `NULL`. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE, @ref GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. * * @remark The contents of the returned string may change when a keyboard * layout change event is received. @@ -4062,15 +4064,18 @@ GLFWAPI const char* glfwGetKeyName(int key, int scancode); * * This function returns the platform-specific scancode of the specified key. * - * If the key is `GLFW_KEY_UNKNOWN` or does not exist on the keyboard this - * method will return `-1`. + * If the specified [key token](@ref keys) corresponds to a physical key not + * supported on the current platform then this method will return `-1`. + * Calling this function with anything other than a key token will return `-1` + * and generate a @ref GLFW_INVALID_ENUM error. * - * @param[in] key Any [named key](@ref keys). - * @return The platform-specific scancode for the key, or `-1` if an - * [error](@ref error_handling) occurred. + * @param[in] key Any [key token](@ref keys). + * @return The platform-specific scancode for the key, or `-1` if the key is + * not supported on the current platform or an [error](@ref error_handling) + * occurred. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. * * @thread_safety This function may be called from any thread. * @@ -4354,10 +4359,9 @@ GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor); * [character callback](@ref glfwSetCharCallback) instead. * * When a window loses input focus, it will generate synthetic key release - * events for all pressed keys. You can tell these events from user-generated - * events by the fact that the synthetic ones are generated after the focus - * loss event has been processed, i.e. after the - * [window focus callback](@ref glfwSetWindowFocusCallback) has been called. + * events for all pressed keys with associated key tokens. You can tell these + * events from user-generated events by the fact that the synthetic ones are + * generated after the focus loss event has been processed, i.e. after the * * The scancode of a key is specific to that platform or sometimes even to that * machine. Scancodes are intended to allow users to bind keys that don't have @@ -4708,12 +4712,15 @@ GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); * thread. * * This function makes the OpenGL or OpenGL ES context of the specified window - * current on the calling thread. A context must only be made current on - * a single thread at a time and each thread can have only a single current - * context at a time. + * current on the calling thread. It can also detach the current context from + * the calling thread without making a new one current by passing in `NULL`. * - * When moving a context between threads, you must make it non-current on the - * old thread before making it current on the new one. + * A context must only be made current on a single thread at a time and each + * thread can have only a single current context at a time. Making a context + * current detaches any previously current context on the calling thread. + * + * When moving a context between threads, you must detach it (make it + * non-current) on the old thread before making it current on the new one. * * By default, making a context non-current implicitly forces a pipeline flush. * On machines that support `GL_KHR_context_flush_control`, you can control @@ -4728,6 +4735,10 @@ GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); * @param[in] window The window whose context to make current, or `NULL` to * detach the current context. * + * @remarks If the previously current context was created via a different + * context creation API than the one passed to this function, GLFW will still + * detach the previous one from its API before making the new one current. + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR. * diff --git a/internal/glfw/glfw3native_unix.h b/internal/glfw/glfw3native_unix.h index 565eb7471..4dfd56871 100644 --- a/internal/glfw/glfw3native_unix.h +++ b/internal/glfw/glfw3native_unix.h @@ -99,7 +99,9 @@ extern "C" { #include #include #endif - #elif defined(GLFW_EXPOSE_NATIVE_X11) || defined(GLFW_EXPOSE_NATIVE_GLX) + #endif + + #if defined(GLFW_EXPOSE_NATIVE_X11) || defined(GLFW_EXPOSE_NATIVE_GLX) #include #include #endif diff --git a/internal/glfw/glx_context_linbsd.c b/internal/glfw/glx_context_linbsd.c index 4de3fe2b0..33afbae19 100644 --- a/internal/glfw/glx_context_linbsd.c +++ b/internal/glfw/glx_context_linbsd.c @@ -168,6 +168,7 @@ static void swapBuffersGLX(_GLFWwindow* window) static void swapIntervalGLX(int interval) { _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); + assert(window != NULL); if (_glfw.glx.EXT_swap_control) { @@ -204,7 +205,10 @@ static GLFWglproc getProcAddressGLX(const char* procname) else if (_glfw.glx.GetProcAddressARB) return _glfw.glx.GetProcAddressARB((const GLubyte*) procname); else + { + // NOTE: glvnd provides GLX 1.4, so this can only happen with libGL return _glfw_dlsym(_glfw.glx.handle, procname); + } } static void destroyContextGLX(_GLFWwindow* window) diff --git a/internal/glfw/glx_context_linbsd.h b/internal/glfw/glx_context_linbsd.h index 46f95bc2e..e11d718cf 100644 --- a/internal/glfw/glx_context_linbsd.h +++ b/internal/glfw/glx_context_linbsd.h @@ -107,7 +107,6 @@ typedef struct _GLFWlibraryGLX int eventBase; int errorBase; - // dlopen handle for libGL.so.1 void* handle; // GLX 1.3 functions diff --git a/internal/glfw/input_unix.c b/internal/glfw/input_unix.c index 9d51a4842..08ba0ed7e 100644 --- a/internal/glfw/input_unix.c +++ b/internal/glfw/input_unix.c @@ -280,6 +280,12 @@ GLFWAPI const char* glfwGetKeyName(int key, int scancode) if (key != GLFW_KEY_UNKNOWN) { + if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key); + return NULL; + } + if (key != GLFW_KEY_KP_EQUAL && (key < GLFW_KEY_KP_0 || key > GLFW_KEY_KP_ADD) && (key < GLFW_KEY_APOSTROPHE || key > GLFW_KEY_WORLD_2)) @@ -300,7 +306,7 @@ GLFWAPI int glfwGetKeyScancode(int key) if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST) { _glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key); - return GLFW_RELEASE; + return -1; } return _glfwPlatformGetKeyScancode(key); diff --git a/internal/glfw/input_windows.go b/internal/glfw/input_windows.go index 4632c3189..f8c7c124c 100644 --- a/internal/glfw/input_windows.go +++ b/internal/glfw/input_windows.go @@ -246,6 +246,9 @@ func GetKeyName(key Key, scancode int) (string, error) { } if key != KeyUnknown { + if key < KeySpace || key > KeyLast { + return "", fmt.Errorf("glfw: invalid key %d: %w", key, InvalidEnum) + } if key != KeyKPEqual && (key < KeyKP0 || key > KeyKPAdd) && (key < KeyApostrophe || key > KeyWorld2) { return "", nil } diff --git a/internal/glfw/nsgl_context_darwin.m b/internal/glfw/nsgl_context_darwin.m index 637986531..6b8545d4c 100644 --- a/internal/glfw/nsgl_context_darwin.m +++ b/internal/glfw/nsgl_context_darwin.m @@ -57,11 +57,10 @@ static void swapIntervalNSGL(int interval) @autoreleasepool { _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); - if (window) - { - [window->context.nsgl.object setValues:&interval - forParameter:NSOpenGLContextParameterSwapInterval]; - } + assert(window != NULL); + + [window->context.nsgl.object setValues:&interval + forParameter:NSOpenGLContextParameterSwapInterval]; } // autoreleasepool } diff --git a/internal/glfw/wgl_context_windows.go b/internal/glfw/wgl_context_windows.go index f3aebfb70..e16ec0bf8 100644 --- a/internal/glfw/wgl_context_windows.go +++ b/internal/glfw/wgl_context_windows.go @@ -53,12 +53,13 @@ func (w *Window) choosePixelFormat(ctxconfig *ctxconfig, fbconfig_ *fbconfig) (i var nativeCount int32 var attribs []int32 - if _glfw.platformContext.ARB_pixel_format { - var attrib int32 = _WGL_NUMBER_PIXEL_FORMATS_ARB - if err := wglGetPixelFormatAttribivARB(w.context.platform.dc, 1, 0, 1, &attrib, &nativeCount); err != nil { - return 0, err - } + c, err := _DescribePixelFormat(w.context.platform.dc, 1, uint32(unsafe.Sizeof(_PIXELFORMATDESCRIPTOR{})), nil) + if err != nil { + return 0, err + } + nativeCount = c + if _glfw.platformContext.ARB_pixel_format { attribs = append(attribs, _WGL_SUPPORT_OPENGL_ARB, _WGL_DRAW_TO_WINDOW_ARB, @@ -96,12 +97,6 @@ func (w *Window) choosePixelFormat(ctxconfig *ctxconfig, fbconfig_ *fbconfig) (i attribs = append(attribs, _WGL_COLORSPACE_EXT) } } - } else { - c, err := _DescribePixelFormat(w.context.platform.dc, 1, uint32(unsafe.Sizeof(_PIXELFORMATDESCRIPTOR{})), nil) - if err != nil { - return 0, err - } - nativeCount = c } usableConfigs := make([]*fbconfig, 0, nativeCount) diff --git a/internal/glfw/win32_platform_windows.go b/internal/glfw/win32_platform_windows.go index f4f5bb3f4..7c1e50e1f 100644 --- a/internal/glfw/win32_platform_windows.go +++ b/internal/glfw/win32_platform_windows.go @@ -72,6 +72,8 @@ type platformLibraryWindowState struct { // The window whose disabled cursor mode is active disabledCursorWindow *Window + // The window the cursor is captured in + capturedCursorWindow *Window rawInput []byte mouseTrailSize uint32 } diff --git a/internal/glfw/win32_window_windows.go b/internal/glfw/win32_window_windows.go index e1f8ef101..92ecbd173 100644 --- a/internal/glfw/win32_window_windows.go +++ b/internal/glfw/win32_window_windows.go @@ -127,48 +127,29 @@ func createIcon(image *Image, xhot, yhot int, icon bool) (_HICON, error) { return handle, nil } -func getFullWindowSize(style uint32, exStyle uint32, contentWidth, contentHeight int, dpi uint32) (fullWidth, fullHeight int, err error) { - if microsoftgdk.IsXbox() { - return contentWidth, contentHeight, nil - } +func (w *Window) applyAspectRatio(edge int, area *_RECT) error { + var frame _RECT + + ratio := float32(w.numer) / float32(w.denom) + style := w.getWindowStyle() + exStyle := w.getWindowExStyle() - rect := _RECT{ - left: 0, - top: 0, - right: int32(contentWidth), - bottom: int32(contentHeight), - } if winver.IsWindows10AnniversaryUpdateOrGreater() { - if err := _AdjustWindowRectExForDpi(&rect, style, false, exStyle, dpi); err != nil { - return 0, 0, err + if err := _AdjustWindowRectExForDpi(&frame, style, false, exStyle, _GetDpiForWindow(w.platform.handle)); err != nil { + return err } } else { - if err := _AdjustWindowRectEx(&rect, style, false, exStyle); err != nil { - return 0, 0, err + if err := _AdjustWindowRectEx(&frame, style, false, exStyle); err != nil { + return err } } - return int(rect.right - rect.left), int(rect.bottom - rect.top), nil -} - -func (w *Window) applyAspectRatio(edge int, area *_RECT) error { - ratio := float32(w.numer) / float32(w.denom) - - var dpi uint32 = _USER_DEFAULT_SCREEN_DPI - if winver.IsWindows10AnniversaryUpdateOrGreater() { - dpi = _GetDpiForWindow(w.platform.handle) - } - - xoff, yoff, err := getFullWindowSize(w.getWindowStyle(), w.getWindowExStyle(), 0, 0, dpi) - if err != nil { - return err - } if edge == _WMSZ_LEFT || edge == _WMSZ_BOTTOMLEFT || edge == _WMSZ_RIGHT || edge == _WMSZ_BOTTOMRIGHT { - area.bottom = area.top + int32(yoff) + int32(float32(area.right-area.left-int32(xoff))/ratio) + area.bottom = area.top + int32(frame.bottom-frame.top) + int32(float32(area.right-area.left-int32(frame.right-frame.left))/ratio) } else if edge == _WMSZ_TOPLEFT || edge == _WMSZ_TOPRIGHT { - area.top = area.bottom - int32(yoff) - int32(float32(area.right-area.left-int32(xoff))/ratio) + area.top = area.bottom - int32(frame.bottom-frame.top) - int32(float32(area.right-area.left-int32(frame.right-frame.left))/ratio) } else if edge == _WMSZ_TOP || edge == _WMSZ_BOTTOM { - area.right = area.left + int32(xoff) + int32(float32(area.bottom-area.top-int32(yoff))*ratio) + area.right = area.left + int32(frame.right-frame.left) + int32(float32(area.bottom-area.top-int32(frame.bottom-frame.top))*ratio) } return nil @@ -214,26 +195,27 @@ func (w *Window) clientToScreen(rect _RECT) (_RECT, error) { return rect, nil } -func updateClipRect(window *Window) error { - if window != nil { - clipRect, err := _GetClientRect(window.platform.handle) - if err != nil { - return err - } - - clipRect, err = window.clientToScreen(clipRect) - if err != nil { - return err - } - - if err := _ClipCursor(&clipRect); err != nil { - return err - } - } else { - if err := _ClipCursor(nil); err != nil { - return err - } +func captureCursor(window *Window) error { + clipRect, err := _GetClientRect(window.platform.handle) + if err != nil { + return err } + clipRect, err = window.clientToScreen(clipRect) + if err != nil { + return err + } + if err := _ClipCursor(&clipRect); err != nil { + return err + } + _glfw.platformWindow.capturedCursorWindow = window + return nil +} + +func releaseCursor() error { + if err := _ClipCursor(nil); err != nil { + return err + } + _glfw.platformWindow.capturedCursorWindow = nil return nil } @@ -274,7 +256,7 @@ func (w *Window) disableCursor() error { if err := w.centerCursorInContentArea(); err != nil { return err } - if err := updateClipRect(w); err != nil { + if err := captureCursor(w); err != nil { return err } if w.rawMouseMotion { @@ -292,7 +274,7 @@ func (w *Window) enableCursor() error { } } _glfw.platformWindow.disabledCursorWindow = nil - if err := updateClipRect(nil); err != nil { + if err := releaseCursor(); err != nil { return err } if err := w.platformSetCursorPos(_glfw.platformWindow.restoreCursorPosX, _glfw.platformWindow.restoreCursorPosY); err != nil { @@ -986,8 +968,8 @@ func windowProc(hWnd windows.HWND, uMsg uint32, wParam _WPARAM, lParam _LPARAM) iconified := wParam == _SIZE_MINIMIZED maximized := wParam == _SIZE_MAXIMIZED || (window.platform.maximized && wParam != _SIZE_RESTORED) - if _glfw.platformWindow.disabledCursorWindow == window { - if err := updateClipRect(window); err != nil { + if _glfw.platformWindow.capturedCursorWindow == window { + if err := captureCursor(window); err != nil { _glfw.errors = append(_glfw.errors, err) return 0 } @@ -1032,8 +1014,8 @@ func windowProc(hWnd windows.HWND, uMsg uint32, wParam _WPARAM, lParam _LPARAM) return 0 case _WM_MOVE: - if _glfw.platformWindow.disabledCursorWindow == window { - if err := updateClipRect(window); err != nil { + if _glfw.platformWindow.capturedCursorWindow == window { + if err := captureCursor(window); err != nil { _glfw.errors = append(_glfw.errors, err) return 0 } @@ -1056,31 +1038,35 @@ func windowProc(hWnd windows.HWND, uMsg uint32, wParam _WPARAM, lParam _LPARAM) return 1 case _WM_GETMINMAXINFO: - var dpi uint32 = _USER_DEFAULT_SCREEN_DPI + var frame _RECT mmi := (*_MINMAXINFO)(unsafe.Pointer(lParam)) + style := window.getWindowStyle() + exStyle := window.getWindowExStyle() if window.monitor != nil { break } if winver.IsWindows10AnniversaryUpdateOrGreater() { - dpi = _GetDpiForWindow(window.platform.handle) - } - - xoff, yoff, err := getFullWindowSize(window.getWindowStyle(), window.getWindowExStyle(), 0, 0, dpi) - if err != nil { - _glfw.errors = append(_glfw.errors, err) - return 0 + if err := _AdjustWindowRectExForDpi(&frame, style, false, exStyle, _GetDpiForWindow(window.platform.handle)); err != nil { + _glfw.errors = append(_glfw.errors, err) + return 0 + } + } else { + if err := _AdjustWindowRectEx(&frame, style, false, exStyle); err != nil { + _glfw.errors = append(_glfw.errors, err) + return 0 + } } if window.minwidth != DontCare && window.minheight != DontCare { - mmi.ptMinTrackSize.x = int32(window.minwidth + xoff) - mmi.ptMinTrackSize.y = int32(window.minheight + yoff) + mmi.ptMinTrackSize.x = int32(window.minwidth) + (frame.right - frame.left) + mmi.ptMinTrackSize.y = int32(window.minheight) + (frame.bottom - frame.top) } if window.maxwidth != DontCare && window.maxheight != DontCare { - mmi.ptMaxTrackSize.x = int32(window.maxwidth + xoff) - mmi.ptMaxTrackSize.y = int32(window.maxheight + yoff) + mmi.ptMaxTrackSize.x = int32(window.maxwidth) + (frame.right - frame.left) + mmi.ptMaxTrackSize.y = int32(window.maxheight) + (frame.bottom - frame.top) } if !window.decorated { @@ -1205,7 +1191,7 @@ func (w *Window) createNativeWindow(wndconfig *wndconfig, fbconfig *fbconfig) er style := w.getWindowStyle() exStyle := w.getWindowExStyle() - var xpos, ypos, fullWidth, fullHeight int32 + var frameX, frameY, frameWidth, frameHeight int32 if w.monitor != nil { mi, ok := _GetMonitorInfoW(w.monitor.platform.handle) if !ok { @@ -1214,27 +1200,29 @@ func (w *Window) createNativeWindow(wndconfig *wndconfig, fbconfig *fbconfig) er // NOTE: This window placement is temporary and approximate, as the // correct position and size cannot be known until the monitor // video mode has been picked in _glfwSetVideoModeWin32 - xpos = mi.rcMonitor.left - ypos = mi.rcMonitor.top - fullWidth = mi.rcMonitor.right - mi.rcMonitor.left - fullHeight = mi.rcMonitor.bottom - mi.rcMonitor.top + frameX = mi.rcMonitor.left + frameY = mi.rcMonitor.top + frameWidth = mi.rcMonitor.right - mi.rcMonitor.left + frameHeight = mi.rcMonitor.bottom - mi.rcMonitor.top } else { - xpos = _CW_USEDEFAULT - ypos = _CW_USEDEFAULT + rect := _RECT{0, 0, int32(wndconfig.width), int32(wndconfig.height)} w.platform.maximized = wndconfig.maximized if wndconfig.maximized { style |= _WS_MAXIMIZE } - w, h, err := getFullWindowSize(style, exStyle, wndconfig.width, wndconfig.height, _USER_DEFAULT_SCREEN_DPI) - if err != nil { + if err := _AdjustWindowRectEx(&rect, style, false, exStyle); err != nil { return err } - fullWidth, fullHeight = int32(w), int32(h) + + frameX = _CW_USEDEFAULT + frameY = _CW_USEDEFAULT + frameWidth = rect.right - rect.left + frameHeight = rect.bottom - rect.top } - h, err := _CreateWindowExW(exStyle, _GLFW_WNDCLASSNAME, wndconfig.title, style, xpos, ypos, fullWidth, fullHeight, + h, err := _CreateWindowExW(exStyle, _GLFW_WNDCLASSNAME, wndconfig.title, style, frameX, frameY, frameWidth, frameHeight, 0, // No parent window 0, // No window menu _glfw.platformWindow.instance, unsafe.Pointer(wndconfig)) @@ -1459,7 +1447,15 @@ func (w *Window) platformDestroyWindow() error { } if _glfw.platformWindow.disabledCursorWindow == w { - _glfw.platformWindow.disabledCursorWindow = nil + if err := w.enableCursor(); err != nil { + return err + } + } + + if _glfw.platformWindow.capturedCursorWindow == w { + if err := releaseCursor(); err != nil { + return err + } } if w.platform.handle != 0 { @@ -2184,7 +2180,7 @@ func platformWaitEvents() error { } func platformWaitEventsTimeout(timeout float64) error { - if _, err := _MsgWaitForMultipleObjects(0, nil, false, uint32(timeout*1e3), _QS_ALLEVENTS); err != nil { + if _, err := _MsgWaitForMultipleObjects(0, nil, false, uint32(timeout*1e3), _QS_ALLINPUT); err != nil { return err } if err := platformPollEvents(); err != nil { @@ -2235,20 +2231,48 @@ func (w *Window) platformSetCursorPos(xpos, ypos float64) error { } func (w *Window) platformSetCursorMode(mode int) error { - if mode == CursorDisabled { - if w.platformWindowFocused() { - if err := w.disableCursor(); err != nil { + if w.platformWindowFocused() { + if mode == CursorDisabled { + xpos, ypos, err := w.platformGetCursorPos() + if err != nil { + return err + } + _glfw.platformWindow.restoreCursorPosX = xpos + _glfw.platformWindow.restoreCursorPosY = ypos + if err := w.centerCursorInContentArea(); err != nil { + return err + } + if w.rawMouseMotion { + if err := w.enableRawMouseMotion(); err != nil { + return err + } + } + } else if _glfw.platformWindow.disabledCursorWindow == w { + if w.rawMouseMotion { + if err := w.disableRawMouseMotion(); err != nil { + return err + } + } + } + + if mode == CursorDisabled { + if err := captureCursor(w); err != nil { + return err + } + } else { + if err := releaseCursor(); err != nil { return err } } - return nil - } - if _glfw.platformWindow.disabledCursorWindow == w { - if err := w.enableCursor(); err != nil { - return err + if mode == CursorDisabled { + _glfw.platformWindow.disabledCursorWindow = w + } else { + _glfw.platformWindow.disabledCursorWindow = nil + if err := w.platformSetCursorPos(_glfw.platformWindow.restoreCursorPosX, _glfw.platformWindow.restoreCursorPosY); err != nil { + return err + } } - return nil } in, err := w.cursorInContentArea() diff --git a/internal/glfw/x11_window_linbsd.c b/internal/glfw/x11_window_linbsd.c index b221823ac..3e72d4e3d 100644 --- a/internal/glfw/x11_window_linbsd.c +++ b/internal/glfw/x11_window_linbsd.c @@ -357,6 +357,11 @@ static void updateNormalHints(_GLFWwindow* window, int width, int height) { XSizeHints* hints = XAllocSizeHints(); + long supplied; + XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied); + + hints->flags &= ~(PMinSize | PMaxSize | PAspect); + if (!window->monitor) { if (window->resizable) @@ -393,9 +398,6 @@ static void updateNormalHints(_GLFWwindow* window, int width, int height) } } - hints->flags |= PWinGravity; - hints->win_gravity = StaticGravity; - XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints); XFree(hints); @@ -561,6 +563,25 @@ static void updateCursorImage(_GLFWwindow* window) } } +// Grabs the cursor and confines it to the window +// +static void captureCursor(_GLFWwindow* window) +{ + XGrabPointer(_glfw.x11.display, window->x11.handle, True, + ButtonPressMask | ButtonReleaseMask | PointerMotionMask, + GrabModeAsync, GrabModeAsync, + window->x11.handle, + None, + CurrentTime); +} + +// Ungrabs the cursor +// +static void releaseCursor(void) +{ + XUngrabPointer(_glfw.x11.display, CurrentTime); +} + // Enable XI2 raw mouse motion events // static void enableRawMouseMotion(_GLFWwindow* window) @@ -603,12 +624,7 @@ static void disableCursor(_GLFWwindow* window) &_glfw.x11.restoreCursorPosY); updateCursorImage(window); _glfwCenterCursorInContentArea(window); - XGrabPointer(_glfw.x11.display, window->x11.handle, True, - ButtonPressMask | ButtonReleaseMask | PointerMotionMask, - GrabModeAsync, GrabModeAsync, - window->x11.handle, - _glfw.x11.hiddenCursorHandle, - CurrentTime); + captureCursor(window); } // Exit disabled cursor mode for the specified window @@ -619,7 +635,7 @@ static void enableCursor(_GLFWwindow* window) disableRawMouseMotion(window); _glfw.x11.disabledCursorWindow = NULL; - XUngrabPointer(_glfw.x11.display, CurrentTime); + releaseCursor(); _glfwPlatformSetCursorPos(window, _glfw.x11.restoreCursorPosX, _glfw.x11.restoreCursorPosY); @@ -764,7 +780,28 @@ static GLFWbool createNativeWindow(_GLFWwindow* window, XFree(hints); } - updateNormalHints(window, width, height); + // Set ICCCM WM_NORMAL_HINTS property + { + XSizeHints* hints = XAllocSizeHints(); + if (!hints) + { + _glfwInputError(GLFW_OUT_OF_MEMORY, "X11: Failed to allocate size hints"); + return GLFW_FALSE; + } + + if (!wndconfig->resizable) + { + hints->flags |= (PMinSize | PMaxSize); + hints->min_width = hints->max_width = width; + hints->min_height = hints->max_height = height; + } + + hints->flags |= PWinGravity; + hints->win_gravity = StaticGravity; + + XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints); + XFree(hints); + } // Set ICCCM WM_CLASS property { @@ -2085,7 +2122,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, void _glfwPlatformDestroyWindow(_GLFWwindow* window) { if (_glfw.x11.disabledCursorWindow == window) - _glfw.x11.disabledCursorWindow = NULL; + enableCursor(window); if (window->monitor) releaseMonitor(window); @@ -2891,16 +2928,40 @@ void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) { - if (mode == GLFW_CURSOR_DISABLED) + if (_glfwPlatformWindowFocused(window)) { - if (_glfwPlatformWindowFocused(window)) - disableCursor(window); - } - else if (_glfw.x11.disabledCursorWindow == window) - enableCursor(window); - else - updateCursorImage(window); + if (mode == GLFW_CURSOR_DISABLED) + { + _glfwPlatformGetCursorPos(window, + &_glfw.x11.restoreCursorPosX, + &_glfw.x11.restoreCursorPosY); + _glfwCenterCursorInContentArea(window); + if (window->rawMouseMotion) + enableRawMouseMotion(window); + } + else if (_glfw.x11.disabledCursorWindow == window) + { + if (window->rawMouseMotion) + disableRawMouseMotion(window); + } + if (mode == GLFW_CURSOR_DISABLED) + captureCursor(window); + else + releaseCursor(); + + if (mode == GLFW_CURSOR_DISABLED) + _glfw.x11.disabledCursorWindow = window; + else if (_glfw.x11.disabledCursorWindow == window) + { + _glfw.x11.disabledCursorWindow = NULL; + _glfwPlatformSetCursorPos(window, + _glfw.x11.restoreCursorPosX, + _glfw.x11.restoreCursorPosY); + } + } + + updateCursorImage(window); XFlush(_glfw.x11.display); } diff --git a/internal/graphicsdriver/opengl/gl/procaddr_linbsd.go b/internal/graphicsdriver/opengl/gl/procaddr_linbsd.go index 3047376c6..a7b40dac4 100644 --- a/internal/graphicsdriver/opengl/gl/procaddr_linbsd.go +++ b/internal/graphicsdriver/opengl/gl/procaddr_linbsd.go @@ -68,6 +68,9 @@ func (c *defaultContext) init() error { // Try OpenGL first. OpenGL is preferable as this doesn't cause context losses. if !preferES { // Usually libGL.so or libGL.so.1 is used. libGL.so.2 might exist only on NetBSD. + // TODO: Should "libOpenGL.so.0" [1] and "libGLX.so.0" [2] be added? These were added as of GLFW 3.3.9. + // [1] https://github.com/glfw/glfw/commit/55aad3c37b67f17279378db52da0a3ab81bbf26d + // [2] https://github.com/glfw/glfw/commit/c18851f52ec9704eb06464058a600845ec1eada1 for _, name := range []string{"libGL.so", "libGL.so.2", "libGL.so.1", "libGL.so.0"} { cname := C.CString(name) lib := C.dlopen(cname, C.RTLD_LAZY|C.RTLD_GLOBAL) From 4dfb3d2fc1dbce63fbc39f56cbad611e081fa0a6 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 14 Apr 2024 13:32:41 +0900 Subject: [PATCH 02/81] internal/glfw, internal/gamepad: update GLFW to v3.3.10 Closes #2960 --- internal/glfw/cocoa_window_darwin.m | 5 +++-- internal/glfw/glfw3_unix.h | 12 ++++++++---- internal/glfw/input_unix.c | 2 +- internal/glfw/nsgl_context_darwin.m | 2 +- internal/glfw/osmesa_context_unix.c | 5 ++--- internal/glfw/wgl_context_windows.go | 12 ++++++++++++ internal/glfw/win32_window_windows.go | 8 ++++++-- internal/glfw/window_unix.c | 2 +- internal/glfw/x11_window_linbsd.c | 5 +++-- 9 files changed, 37 insertions(+), 16 deletions(-) diff --git a/internal/glfw/cocoa_window_darwin.m b/internal/glfw/cocoa_window_darwin.m index 1b1a7ebf1..104817978 100644 --- a/internal/glfw/cocoa_window_darwin.m +++ b/internal/glfw/cocoa_window_darwin.m @@ -1621,14 +1621,15 @@ const char* _glfwPlatformGetScancodeName(int scancode) { @autoreleasepool { - if (scancode < 0 || scancode > 0xff || - _glfw.ns.keycodes[scancode] == GLFW_KEY_UNKNOWN) + if (scancode < 0 || scancode > 0xff) { _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode %i", scancode); return NULL; } const int key = _glfw.ns.keycodes[scancode]; + if (key == GLFW_KEY_UNKNOWN) + return NULL; UInt32 deadKeyState = 0; UniChar characters[4]; diff --git a/internal/glfw/glfw3_unix.h b/internal/glfw/glfw3_unix.h index 9070de920..b51a12adf 100644 --- a/internal/glfw/glfw3_unix.h +++ b/internal/glfw/glfw3_unix.h @@ -249,7 +249,7 @@ extern "C" { * release is made that does not contain any API changes. * @ingroup init */ -#define GLFW_VERSION_REVISION 9 +#define GLFW_VERSION_REVISION 10 /*! @} */ /*! @brief One. @@ -2412,8 +2412,8 @@ GLFWAPI void glfwWindowHintString(int hint, const char* value); * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE, @ref GLFW_API_UNAVAILABLE, @ref - * GLFW_VERSION_UNAVAILABLE, @ref GLFW_FORMAT_UNAVAILABLE and @ref - * GLFW_PLATFORM_ERROR. + * GLFW_VERSION_UNAVAILABLE, @ref GLFW_FORMAT_UNAVAILABLE, @ref + * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR. * * @remark @win32 Window creation will fail if the Microsoft GDI software * OpenGL implementation is the only one available. @@ -3362,11 +3362,15 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib); * @param[in] value `GLFW_TRUE` or `GLFW_FALSE`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE, @ref GLFW_PLATFORM_ERROR. * * @remark Calling @ref glfwGetWindowAttrib will always return the latest * value, even if that value is ignored by the current mode of the window. * + * @remark @wayland The [GLFW_FLOATING](@ref GLFW_FLOATING_attrib) window + * attribute is not supported. Setting this will emit @ref + * GLFW_PLATFORM_ERROR. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_attribs diff --git a/internal/glfw/input_unix.c b/internal/glfw/input_unix.c index 08ba0ed7e..099a35772 100644 --- a/internal/glfw/input_unix.c +++ b/internal/glfw/input_unix.c @@ -301,7 +301,7 @@ GLFWAPI const char* glfwGetKeyName(int key, int scancode) GLFWAPI int glfwGetKeyScancode(int key) { - _GLFW_REQUIRE_INIT_OR_RETURN(-1); + _GLFW_REQUIRE_INIT_OR_RETURN(0); if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST) { diff --git a/internal/glfw/nsgl_context_darwin.m b/internal/glfw/nsgl_context_darwin.m index 6b8545d4c..3d96557f5 100644 --- a/internal/glfw/nsgl_context_darwin.m +++ b/internal/glfw/nsgl_context_darwin.m @@ -137,7 +137,7 @@ GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, if (ctxconfig->client == GLFW_OPENGL_ES_API) { _glfwInputError(GLFW_API_UNAVAILABLE, - "NSGL: OpenGL ES is not available on macOS"); + "NSGL: OpenGL ES is not available via NSGL"); return GLFW_FALSE; } diff --git a/internal/glfw/osmesa_context_unix.c b/internal/glfw/osmesa_context_unix.c index 950fbfa3c..787c3075b 100644 --- a/internal/glfw/osmesa_context_unix.c +++ b/internal/glfw/osmesa_context_unix.c @@ -5,13 +5,12 @@ //go:build darwin || freebsd || linux || netbsd || openbsd +#include "internal_unix.h" + #include #include #include -#include "internal_unix.h" - - static void makeContextCurrentOSMesa(_GLFWwindow* window) { if (window) diff --git a/internal/glfw/wgl_context_windows.go b/internal/glfw/wgl_context_windows.go index e16ec0bf8..305755f97 100644 --- a/internal/glfw/wgl_context_windows.go +++ b/internal/glfw/wgl_context_windows.go @@ -60,6 +60,18 @@ func (w *Window) choosePixelFormat(ctxconfig *ctxconfig, fbconfig_ *fbconfig) (i nativeCount = c if _glfw.platformContext.ARB_pixel_format { + // NOTE: In a Parallels VM WGL_ARB_pixel_format returns fewer pixel formats than + // DescribePixelFormat, violating the guarantees of the extension spec + // HACK: Iterate through the minimum of both counts + var attrib int32 = _WGL_NUMBER_PIXEL_FORMATS_ARB + var extensionCount int32 + if err := wglGetPixelFormatAttribivARB(w.context.platform.dc, 1, 0, 1, &attrib, &extensionCount); err != nil { + return 0, fmt.Errorf("glfw: WGL: failed to retrieve pixel format attribute: %w", err) + } + if nativeCount > extensionCount { + nativeCount = extensionCount + } + attribs = append(attribs, _WGL_SUPPORT_OPENGL_ARB, _WGL_DRAW_TO_WINDOW_ARB, diff --git a/internal/glfw/win32_window_windows.go b/internal/glfw/win32_window_windows.go index 92ecbd173..3bba41122 100644 --- a/internal/glfw/win32_window_windows.go +++ b/internal/glfw/win32_window_windows.go @@ -2289,10 +2289,14 @@ func (w *Window) platformSetCursorMode(mode int) error { } func platformGetScancodeName(scancode int) (string, error) { - if scancode < 0 || scancode > (_KF_EXTENDED|0xff) || _glfw.platformWindow.keycodes[scancode] == KeyUnknown { + if scancode < 0 || scancode > (_KF_EXTENDED|0xff) { return "", fmt.Errorf("glwfwin: invalid scancode %d: %w", scancode, InvalidValue) } - return _glfw.platformWindow.keynames[_glfw.platformWindow.keycodes[scancode]], nil + key := _glfw.platformWindow.keycodes[scancode] + if key == KeyUnknown { + return "", nil + } + return _glfw.platformWindow.keynames[key], nil } func platformGetKeyScancode(key Key) int { diff --git a/internal/glfw/window_unix.c b/internal/glfw/window_unix.c index 494b557f1..1e1d4556d 100644 --- a/internal/glfw/window_unix.c +++ b/internal/glfw/window_unix.c @@ -676,7 +676,7 @@ GLFWAPI float glfwGetWindowOpacity(GLFWwindow* handle) _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); - _GLFW_REQUIRE_INIT_OR_RETURN(1.f); + _GLFW_REQUIRE_INIT_OR_RETURN(0.f); return _glfwPlatformGetWindowOpacity(window); } diff --git a/internal/glfw/x11_window_linbsd.c b/internal/glfw/x11_window_linbsd.c index 3e72d4e3d..23e88a06f 100644 --- a/internal/glfw/x11_window_linbsd.c +++ b/internal/glfw/x11_window_linbsd.c @@ -2970,14 +2970,15 @@ const char* _glfwPlatformGetScancodeName(int scancode) if (!_glfw.x11.xkb.available) return NULL; - if (scancode < 0 || scancode > 0xff || - _glfw.x11.keycodes[scancode] == GLFW_KEY_UNKNOWN) + if (scancode < 0 || scancode > 0xff) { _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode %i", scancode); return NULL; } const int key = _glfw.x11.keycodes[scancode]; + if (key == GLFW_KEY_UNKNOWN) + return NULL; const KeySym keysym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, _glfw.x11.xkb.group, 0); if (keysym == NoSymbol) From 6ff9e2b44cee834aa4a0c1e45deeb237c533f0c3 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 14 Apr 2024 14:30:04 +0900 Subject: [PATCH 03/81] internal/glfw: bug fix: too early event emitting This change applies the bug fix at glfw/glfw#2046. --- internal/glfw/x11_window_linbsd.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/internal/glfw/x11_window_linbsd.c b/internal/glfw/x11_window_linbsd.c index 23e88a06f..db3f04bd8 100644 --- a/internal/glfw/x11_window_linbsd.c +++ b/internal/glfw/x11_window_linbsd.c @@ -1600,6 +1600,9 @@ static void processEvent(XEvent *event) if (event->xconfigure.width != window->x11.width || event->xconfigure.height != window->x11.height) { + window->x11.width = event->xconfigure.width; + window->x11.height = event->xconfigure.height; + _glfwInputFramebufferSize(window, event->xconfigure.width, event->xconfigure.height); @@ -1607,9 +1610,6 @@ static void processEvent(XEvent *event) _glfwInputWindowSize(window, event->xconfigure.width, event->xconfigure.height); - - window->x11.width = event->xconfigure.width; - window->x11.height = event->xconfigure.height; } int xpos = event->xconfigure.x; @@ -1637,9 +1637,10 @@ static void processEvent(XEvent *event) if (xpos != window->x11.xpos || ypos != window->x11.ypos) { - _glfwInputWindowPos(window, xpos, ypos); window->x11.xpos = xpos; window->x11.ypos = ypos; + + _glfwInputWindowPos(window, xpos, ypos); } return; From d5f15f9354cd049d0a62cce25b25137dcf651de3 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 14 Apr 2024 14:44:38 +0900 Subject: [PATCH 04/81] internal/glfw: bug fix: posting an empty event from different goroutine fails This change applies the bug fix at glfw/glfw#1649 After this change, an Ebitengine application always shows the menu bar, but this should be fine for Ebitengine applications. --- internal/glfw/cocoa_init_darwin.m | 8 ++++---- internal/glfw/cocoa_platform_darwin.h | 1 - internal/glfw/cocoa_window_darwin.m | 15 --------------- internal/glfw/glfw3_unix.h | 15 ++++++++------- 4 files changed, 12 insertions(+), 27 deletions(-) diff --git a/internal/glfw/cocoa_init_darwin.m b/internal/glfw/cocoa_init_darwin.m index a9780d165..af1c5d314 100644 --- a/internal/glfw/cocoa_init_darwin.m +++ b/internal/glfw/cocoa_init_darwin.m @@ -420,7 +420,6 @@ static GLFWbool initializeTIS(void) - (void)applicationDidFinishLaunching:(NSNotification *)notification { - _glfw.ns.finishedLaunching = GLFW_TRUE; _glfwPlatformPostEmptyEvent(); // In case we are unbundled, make us a proper UI application @@ -455,9 +454,6 @@ int _glfwPlatformInit(void) toTarget:_glfw.ns.helper withObject:nil]; - if (NSApp) - _glfw.ns.finishedLaunching = GLFW_TRUE; - [NSApplication sharedApplication]; _glfw.ns.delegate = [[GLFWApplicationDelegate alloc] init]; @@ -509,6 +505,10 @@ int _glfwPlatformInit(void) _glfwInitTimerNS(); _glfwPollMonitorsNS(); + + if (![[NSRunningApplication currentApplication] isFinishedLaunching]) + [NSApp run]; + return GLFW_TRUE; } // autoreleasepool diff --git a/internal/glfw/cocoa_platform_darwin.h b/internal/glfw/cocoa_platform_darwin.h index ef7a7b018..add6b0ebc 100644 --- a/internal/glfw/cocoa_platform_darwin.h +++ b/internal/glfw/cocoa_platform_darwin.h @@ -109,7 +109,6 @@ typedef struct _GLFWlibraryNS { CGEventSourceRef eventSource; id delegate; - GLFWbool finishedLaunching; GLFWbool cursorHidden; TISInputSourceRef inputSource; id unicodeData; diff --git a/internal/glfw/cocoa_window_darwin.m b/internal/glfw/cocoa_window_darwin.m index 104817978..15e461e47 100644 --- a/internal/glfw/cocoa_window_darwin.m +++ b/internal/glfw/cocoa_window_darwin.m @@ -883,9 +883,6 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window, { @autoreleasepool { - if (!_glfw.ns.finishedLaunching) - [NSApp run]; - if (!createNativeWindow(window, wndconfig, fbconfig)) return GLFW_FALSE; @@ -1477,9 +1474,6 @@ void _glfwPlatformPollEvents(void) { @autoreleasepool { - if (!_glfw.ns.finishedLaunching) - [NSApp run]; - for (;;) { NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny @@ -1499,9 +1493,6 @@ void _glfwPlatformWaitEvents(void) { @autoreleasepool { - if (!_glfw.ns.finishedLaunching) - [NSApp run]; - // I wanted to pass NO to dequeue:, and rely on PollEvents to // dequeue and send. For reasons not at all clear to me, passing // NO to dequeue: causes this method never to return. @@ -1520,9 +1511,6 @@ void _glfwPlatformWaitEventsTimeout(double timeout) { @autoreleasepool { - if (!_glfw.ns.finishedLaunching) - [NSApp run]; - NSDate* date = [NSDate dateWithTimeIntervalSinceNow:timeout]; NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:date @@ -1540,9 +1528,6 @@ void _glfwPlatformPostEmptyEvent(void) { @autoreleasepool { - if (!_glfw.ns.finishedLaunching) - [NSApp run]; - NSEvent* event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined location:NSMakePoint(0, 0) modifierFlags:0 diff --git a/internal/glfw/glfw3_unix.h b/internal/glfw/glfw3_unix.h index b51a12adf..7386382a4 100644 --- a/internal/glfw/glfw3_unix.h +++ b/internal/glfw/glfw3_unix.h @@ -1592,6 +1592,14 @@ typedef struct GLFWimage * bundle, if present. This can be disabled with the @ref * GLFW_COCOA_CHDIR_RESOURCES init hint. * + * @remark @macos This function will create the main menu and dock icon for the + * application. If GLFW finds a `MainMenu.nib` it is loaded and assumed to + * contain a menu bar. Otherwise a minimal menu bar is created manually with + * common commands like Hide, Quit and About. The About entry opens a minimal + * about dialog with information from the application's bundle. The menu bar + * and dock icon can be disabled entirely with the @ref GLFW_COCOA_MENUBAR init + * hint. + * * @remark @x11 This function will set the `LC_CTYPE` category of the * application locale according to the current environment if that category is * still "C". This is because the "C" locale breaks Unicode text input. @@ -2439,13 +2447,6 @@ GLFWAPI void glfwWindowHintString(int hint, const char* value); * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) * in the Mac Developer Library. * - * @remark @macos The first time a window is created the menu bar is created. - * If GLFW finds a `MainMenu.nib` it is loaded and assumed to contain a menu - * bar. Otherwise a minimal menu bar is created manually with common commands - * like Hide, Quit and About. The About entry opens a minimal about dialog - * with information from the application's bundle. Menu bar creation can be - * disabled entirely with the @ref GLFW_COCOA_MENUBAR init hint. - * * @remark @macos On OS X 10.10 and later the window frame will not be rendered * at full resolution on Retina displays unless the * [GLFW_COCOA_RETINA_FRAMEBUFFER](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint) From 86e0bcc26472c8da05fac66d84d5dd3869d55e00 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 14 Apr 2024 15:31:10 +0900 Subject: [PATCH 05/81] internal/glfw: bug fix: limit the DWM swap interval to Vista and 7 This change applies the bug fix at glfw/glfw#1072. Updates #2961 --- internal/glfw/wgl_context_windows.go | 45 +++++++++++----------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/internal/glfw/wgl_context_windows.go b/internal/glfw/wgl_context_windows.go index 305755f97..cff168e75 100644 --- a/internal/glfw/wgl_context_windows.go +++ b/internal/glfw/wgl_context_windows.go @@ -261,23 +261,18 @@ func makeContextCurrentWGL(window *Window) error { } func swapBuffersWGL(window *Window) error { - if window.monitor == nil && winver.IsWindowsVistaOrGreater() { - // DWM Composition is always enabled on Win8+ - enabled := winver.IsWindows8OrGreater() - - if !enabled { - var err error - enabled, err = _DwmIsCompositionEnabled() + if window.monitor == nil { + // HACK: Use DwmFlush when desktop composition is enabled on Windows Vista and 7 + if !winver.IsWindows8OrGreater() && winver.IsWindowsVistaOrGreater() { + enabled, err := _DwmIsCompositionEnabled() if err != nil { return err } - } - - // HACK: Use DwmFlush when desktop composition is enabled - if enabled { - for i := 0; i < window.context.platform.interval; i++ { - // Ignore an error from DWM functions as they might not be implemented e.g. on Proton (#2113). - _ = _DwmFlush() + if enabled { + for i := 0; i < window.context.platform.interval; i++ { + // Ignore an error from DWM functions as they might not be implemented e.g. on Proton (#2113). + _ = _DwmFlush() + } } } } @@ -297,22 +292,18 @@ func swapIntervalWGL(interval int) error { window.context.platform.interval = interval - if window.monitor == nil && winver.IsWindowsVistaOrGreater() { - // DWM Composition is always enabled on Win8+ - enabled := winver.IsWindows8OrGreater() - - if !enabled { - e, err := _DwmIsCompositionEnabled() + if window.monitor == nil { + // HACK: Disable WGL swap interval when desktop composition is enabled on Windows + // Vista and 7 to avoid interfering with DWM vsync + if !winver.IsWindows8OrGreater() && winver.IsWindowsVistaOrGreater() { + enabled, err := _DwmIsCompositionEnabled() // Ignore an error from DWM functions as they might not be implemented e.g. on Proton (#2113). if err == nil { - enabled = e + enabled = false + } + if enabled { + interval = 0 } - } - - // HACK: Disable WGL swap interval when desktop composition is enabled to - // avoid interfering with DWM vsync - if enabled { - interval = 0 } } From 6df42f1a4bd87c8944d618736cf152431966c2ef Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 14 Apr 2024 15:58:31 +0900 Subject: [PATCH 06/81] internal/glfw: bug fix: disabling cursor doesn't work well on remote desktop This change applies the bug fix at glfw/glfw#1276 Updates #2961 --- internal/glfw/api_windows.go | 38 ++++++++++++++++ internal/glfw/win32_init_windows.go | 59 +++++++++++++++++++++++++ internal/glfw/win32_platform_windows.go | 10 +++-- internal/glfw/win32_window_windows.go | 48 ++++++++++++++++++-- 4 files changed, 149 insertions(+), 6 deletions(-) diff --git a/internal/glfw/api_windows.go b/internal/glfw/api_windows.go index 6255cd3fa..a9746678b 100644 --- a/internal/glfw/api_windows.go +++ b/internal/glfw/api_windows.go @@ -100,6 +100,7 @@ const ( _MAPVK_VSC_TO_VK = 1 _MONITOR_DEFAULTTONEAREST = 0x00000002 _MOUSE_MOVE_ABSOLUTE = 0x01 + _MOUSE_VIRTUAL_DESKTOP = 0x02 _MSGFLT_ALLOW = 1 _OCR_CROSS = 32515 _OCR_HAND = 32649 @@ -141,11 +142,18 @@ const ( _SIZE_MAXIMIZED = 2 _SIZE_MINIMIZED = 1 _SIZE_RESTORED = 0 + _SM_CXCURSOR = 13 _SM_CXICON = 11 _SM_CXSMICON = 49 _SM_CYCAPTION = 4 + _SM_CYCURSOR = 14 _SM_CYICON = 12 + _SM_CXSCREEN = 0 + _SM_CYSCREEN = 1 _SM_CYSMICON = 50 + _SM_CXVIRTUALSCREEN = 78 + _SM_CYVIRTUALSCREEN = 79 + _SM_REMOTESESSION = 0x1000 _SPI_GETFOREGROUNDLOCKTIMEOUT = 0x2000 _SPI_GETMOUSETRAILS = 94 _SPI_SETFOREGROUNDLOCKTIMEOUT = 0x2001 @@ -755,9 +763,11 @@ var ( procChangeWindowMessageFilterEx = user32.NewProc("ChangeWindowMessageFilterEx") procClientToScreen = user32.NewProc("ClientToScreen") procClipCursor = user32.NewProc("ClipCursor") + procCreateCursor = user32.NewProc("CreateCursor") procCreateIconIndirect = user32.NewProc("CreateIconIndirect") procCreateWindowExW = user32.NewProc("CreateWindowExW") procDefWindowProcW = user32.NewProc("DefWindowProcW") + procDestroyCursor = user32.NewProc("DestroyCursor") procDestroyIcon = user32.NewProc("DestroyIcon") procDestroyWindow = user32.NewProc("DestroyWindow") procDispatchMessageW = user32.NewProc("DispatchMessageW") @@ -915,6 +925,26 @@ func _ClipCursor(lpRect *_RECT) error { return nil } +func _CreateCursor(hInst _HINSTANCE, xHotSpot int32, yHotSpot int32, nWidth int32, nHeight int32, pvANDPlane, pvXORPlane []byte) (_HCURSOR, error) { + var andPlane *byte + if len(pvANDPlane) > 0 { + andPlane = &pvANDPlane[0] + } + var xorPlane *byte + if len(pvXORPlane) > 0 { + xorPlane = &pvXORPlane[0] + } + + r, _, e := procCreateCursor.Call(uintptr(hInst), uintptr(xHotSpot), uintptr(yHotSpot), uintptr(nWidth), uintptr(nHeight), uintptr(unsafe.Pointer(andPlane)), uintptr(unsafe.Pointer(xorPlane))) + runtime.KeepAlive(pvANDPlane) + runtime.KeepAlive(pvXORPlane) + + if _HCURSOR(r) == 0 && !errors.Is(e, windows.ERROR_SUCCESS) { + return 0, fmt.Errorf("glfw: CreateCursor failed: %w", e) + } + return _HCURSOR(r), nil +} + func _CreateBitmap(nWidth int32, nHeight int32, nPlanes uint32, nBitCount uint32, lpBits unsafe.Pointer) (_HBITMAP, error) { r, _, e := procCreateBitmap.Call(uintptr(nWidth), uintptr(nHeight), uintptr(nPlanes), uintptr(nBitCount), uintptr(lpBits)) if _HBITMAP(r) == 0 { @@ -986,6 +1016,14 @@ func _DefWindowProcW(hWnd windows.HWND, uMsg uint32, wParam _WPARAM, lParam _LPA return _LRESULT(r) } +func _DestroyCursor(hCursor _HCURSOR) error { + r, _, e := procDestroyCursor.Call(uintptr(hCursor)) + if int32(r) == 0 && !errors.Is(e, windows.ERROR_SUCCESS) { + return fmt.Errorf("glfw: DestroyCursor failed: %w", e) + } + return nil +} + func _DestroyIcon(hIcon _HICON) error { r, _, e := procDestroyIcon.Call(uintptr(hIcon)) if int32(r) == 0 && !errors.Is(e, windows.ERROR_SUCCESS) { diff --git a/internal/glfw/win32_init_windows.go b/internal/glfw/win32_init_windows.go index 4e7f5eaa7..b33027786 100644 --- a/internal/glfw/win32_init_windows.go +++ b/internal/glfw/win32_init_windows.go @@ -239,6 +239,55 @@ func createHelperWindow() error { return nil } +func createBlankCursor() error { + // HACK: Create a transparent cursor as using the NULL cursor breaks + // using SetCursorPos when connected over RDP + cursorWidth, err := _GetSystemMetrics(_SM_CXCURSOR) + if err != nil { + return err + } + cursorHeight, err := _GetSystemMetrics(_SM_CYCURSOR) + if err != nil { + return err + } + andMask := make([]byte, cursorWidth*cursorHeight/8) + for i := range andMask { + andMask[i] = 0xff + } + xorMask := make([]byte, cursorWidth*cursorHeight/8) + + // Cursor creation might fail, but that's fine as we get NULL in that case, + // which serves as an acceptable fallback blank cursor (other than on RDP) + c, _ := _CreateCursor(0, 0, 0, cursorWidth, cursorHeight, andMask, xorMask) + _glfw.platformWindow.blankCursor = c + + return nil +} + +func initRemoteSession() error { + if microsoftgdk.IsXbox() { + return nil + } + + // Check if the current progress was started with Remote Desktop. + r, err := _GetSystemMetrics(_SM_REMOTESESSION) + if err != nil { + return err + } + _glfw.platformWindow.isRemoteSession = r > 0 + + // With Remote desktop, we need to create a blank cursor because of the cursor is Set to nil + // if cannot be moved to center in capture mode. If not Remote Desktop platformWindow.blankCursor stays nil + // and will perform has before (normal). + if _glfw.platformWindow.isRemoteSession { + if err := createBlankCursor(); err != nil { + return err + } + } + + return nil +} + func platformInit() error { // Changing the foreground lock timeout was removed from the original code. // See https://github.com/glfw/glfw/commit/58b48a3a00d9c2a5ca10cc23069a71d8773cc7a4 @@ -293,6 +342,10 @@ func platformInit() error { return err } } else { + // Some hacks are needed to support Remote Desktop... + if err := initRemoteSession(); err != nil { + return err + } if err := pollMonitorsWin32(); err != nil { return err } @@ -301,6 +354,12 @@ func platformInit() error { } func platformTerminate() error { + if _glfw.platformWindow.blankCursor != 0 { + if err := _DestroyCursor(_glfw.platformWindow.blankCursor); err != nil { + return err + } + } + if _glfw.platformWindow.deviceNotificationHandle != 0 { if err := _UnregisterDeviceNotification(_glfw.platformWindow.deviceNotificationHandle); err != nil { return err diff --git a/internal/glfw/win32_platform_windows.go b/internal/glfw/win32_platform_windows.go index 7c1e50e1f..4199c5dcd 100644 --- a/internal/glfw/win32_platform_windows.go +++ b/internal/glfw/win32_platform_windows.go @@ -66,14 +66,18 @@ type platformLibraryWindowState struct { scancodes [KeyLast + 1]int keynames [KeyLast + 1]string - // Where to place the cursor when re-enabled + // restoreCursorPosX and restoreCursorPosY indicates where to place the cursor when re-enabled restoreCursorPosX float64 restoreCursorPosY float64 - // The window whose disabled cursor mode is active + // disabledCursorWindow is the window whose disabled cursor mode is active disabledCursorWindow *Window - // The window the cursor is captured in + // capturedCursorWindow is the window the cursor is captured in capturedCursorWindow *Window rawInput []byte mouseTrailSize uint32 + // isRemoteSession indicates if the process was started behind Remote Destop + isRemoteSession bool + // blankCursor is an invisible cursor, needed for special cases (see WM_INPUT handler) + blankCursor _HCURSOR } diff --git a/internal/glfw/win32_window_windows.go b/internal/glfw/win32_window_windows.go index 3bba41122..6fcb33356 100644 --- a/internal/glfw/win32_window_windows.go +++ b/internal/glfw/win32_window_windows.go @@ -167,7 +167,10 @@ func (w *Window) updateCursorImage() error { _SetCursor(cursor) } } else { - _SetCursor(0) + // Connected via Remote Desktop, nil cursor will present SetCursorPos the move the cursor. + // using a blank cursor fix that. + // When not via Remote Desktop, platformWindow.blankCursor should be nil. + _SetCursor(_glfw.platformWindow.blankCursor) } return nil } @@ -907,8 +910,46 @@ func windowProc(hWnd windows.HWND, uMsg uint32, wParam _WPARAM, lParam _LPARAM) var dx, dy int data := (*_RAWINPUT)(unsafe.Pointer(&_glfw.platformWindow.rawInput[0])) if data.mouse.usFlags&_MOUSE_MOVE_ABSOLUTE != 0 { - dx = int(data.mouse.lLastX) - window.platform.lastCursorPosX - dy = int(data.mouse.lLastY) - window.platform.lastCursorPosY + if _glfw.platformWindow.isRemoteSession { + // Remote Desktop Mode + // As per https://github.com/Microsoft/DirectXTK/commit/ef56b63f3739381e451f7a5a5bd2c9779d2a7555 + // MOUSE_MOVE_ABSOLUTE is a range from 0 through 65535, based on the screen size. + // Apparently, absolute mode only occurs over RDP though. + var smx int32 = _SM_CXSCREEN + var smy int32 = _SM_CYSCREEN + if data.mouse.usFlags&_MOUSE_VIRTUAL_DESKTOP != 0 { + smx = _SM_CXVIRTUALSCREEN + smy = _SM_CYVIRTUALSCREEN + } + + width, err := _GetSystemMetrics(smx) + if err != nil { + _glfw.errors = append(_glfw.errors, err) + return 0 + } + height, err := _GetSystemMetrics(smy) + if err != nil { + _glfw.errors = append(_glfw.errors, err) + return 0 + } + + pos := _POINT{ + x: int32(float64(data.mouse.lLastX) / 65535.0 * float64(width)), + y: int32(float64(data.mouse.lLastY) / 65535.0 * float64(height)), + } + if err := _ScreenToClient(window.platform.handle, &pos); err != nil { + _glfw.errors = append(_glfw.errors, err) + return 0 + } + + dx = int(pos.x) - window.platform.lastCursorPosX + dy = int(pos.y) - window.platform.lastCursorPosY + } else { + // Normal mode + // We should have the right absolute coords in data.mouse + dx = int(data.mouse.lLastX) - window.platform.lastCursorPosX + dy = int(data.mouse.lLastY) - window.platform.lastCursorPosY + } } else { dx = int(data.mouse.lLastX) dy = int(data.mouse.lLastY) @@ -2159,6 +2200,7 @@ func platformPollEvents() error { // NOTE: Re-center the cursor only if it has moved since the last call, // to avoid breaking glfwWaitEvents with WM_MOUSEMOVE + // The re-center is required in order to prevent the mouse cursor stopping at the edges of the screen. if window.platform.lastCursorPosX != width/2 || window.platform.lastCursorPosY != height/2 { if err := window.platformSetCursorPos(float64(width/2), float64(height/2)); err != nil { return err From 9cd7b34a77a3323badc32e206914dace06af0ca9 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 14 Apr 2024 17:30:21 +0900 Subject: [PATCH 07/81] internal/glfw: reorder constants --- internal/glfw/api_windows.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/glfw/api_windows.go b/internal/glfw/api_windows.go index a9746678b..a8e8e2635 100644 --- a/internal/glfw/api_windows.go +++ b/internal/glfw/api_windows.go @@ -144,11 +144,11 @@ const ( _SIZE_RESTORED = 0 _SM_CXCURSOR = 13 _SM_CXICON = 11 + _SM_CXSCREEN = 0 _SM_CXSMICON = 49 _SM_CYCAPTION = 4 _SM_CYCURSOR = 14 _SM_CYICON = 12 - _SM_CXSCREEN = 0 _SM_CYSCREEN = 1 _SM_CYSMICON = 50 _SM_CXVIRTUALSCREEN = 78 From 11223d9faef571db55b845838642ba00e935184d Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Wed, 17 Apr 2024 15:58:42 +0900 Subject: [PATCH 08/81] text/v2: reuse HarfBuzzShaper Updates #2963 --- text/v2/gotextfacesource.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/text/v2/gotextfacesource.go b/text/v2/gotextfacesource.go index 4964ab06f..65e3d68b4 100644 --- a/text/v2/gotextfacesource.go +++ b/text/v2/gotextfacesource.go @@ -71,6 +71,8 @@ type GoTextFaceSource struct { addr *GoTextFaceSource + shaper shaping.HarfbuzzShaper + m sync.Mutex } @@ -203,7 +205,7 @@ func (g *GoTextFaceSource) shape(text string, face *GoTextFace) ([]shaping.Outpu outputs := make([]shaping.Output, len(inputs)) var gs []glyph for i, input := range inputs { - out := (&shaping.HarfbuzzShaper{}).Shape(input) + out := g.shaper.Shape(input) outputs[i] = out (shaping.Line{out}).AdjustBaselines() From c74e7fa9433d5ae94e96657f3b03d26e303aba8d Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Thu, 18 Apr 2024 13:28:51 +0900 Subject: [PATCH 09/81] internal/gamepaddb: bug fix: platform was not initialized correctly After 6552ae1dbe66d344482990cddb73f6bd0cd3062c, the order of the init function calls changed, and then the platform was not initialized correctly. This change fixes this issue by not relying on an init function to get the platform. Closes #2964 --- internal/gamepaddb/gamepaddb.go | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/internal/gamepaddb/gamepaddb.go b/internal/gamepaddb/gamepaddb.go index 558c9ef4d..617ea2078 100644 --- a/internal/gamepaddb/gamepaddb.go +++ b/internal/gamepaddb/gamepaddb.go @@ -39,12 +39,9 @@ const ( platformIOS ) -var currentPlatform platform - -func init() { +func currentPlatform() platform { if runtime.GOOS == "windows" { - currentPlatform = platformWindows - return + return platformWindows } if runtime.GOOS == "aix" || @@ -56,24 +53,22 @@ func init() { runtime.GOOS == "netbsd" || runtime.GOOS == "openbsd" || runtime.GOOS == "solaris" { - currentPlatform = platformUnix - return + return platformUnix } if runtime.GOOS == "android" { - currentPlatform = platformAndroid - return + return platformAndroid } if runtime.GOOS == "ios" { - currentPlatform = platformIOS - return + return platformIOS } if runtime.GOOS == "darwin" { - currentPlatform = platformMacOS - return + return platformMacOS } + + return platformUnknown } type mappingType int @@ -336,7 +331,7 @@ func buttonMappings(id string) map[StandardButton]mapping { if m, ok := gamepadButtonMappings[id]; ok { return m } - if currentPlatform == platformAndroid { + if currentPlatform() == platformAndroid { if addAndroidDefaultMappings(id) { return gamepadButtonMappings[id] } @@ -348,7 +343,7 @@ func axisMappings(id string) map[StandardAxis]mapping { if m, ok := gamepadAxisMappings[id]; ok { return m } - if currentPlatform == platformAndroid { + if currentPlatform() == platformAndroid { if addAndroidDefaultMappings(id) { return gamepadAxisMappings[id] } @@ -544,7 +539,7 @@ func Update(mappingData []byte) error { for s.Scan() { line := s.Text() - id, name, buttons, axes, err := parseLine(line, currentPlatform) + id, name, buttons, axes, err := parseLine(line, currentPlatform()) if err != nil { return err } From c7eb7371f4261bb985955fc10c0ec4ef68d12d62 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Thu, 18 Apr 2024 13:39:29 +0900 Subject: [PATCH 10/81] internal/gamepad: refactoring --- internal/gamepaddb/gamepaddb.go | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/internal/gamepaddb/gamepaddb.go b/internal/gamepaddb/gamepaddb.go index 617ea2078..4637d669c 100644 --- a/internal/gamepaddb/gamepaddb.go +++ b/internal/gamepaddb/gamepaddb.go @@ -40,35 +40,20 @@ const ( ) func currentPlatform() platform { - if runtime.GOOS == "windows" { + switch runtime.GOOS { + case "windows": return platformWindows - } - - if runtime.GOOS == "aix" || - runtime.GOOS == "dragonfly" || - runtime.GOOS == "freebsd" || - runtime.GOOS == "hurd" || - runtime.GOOS == "illumos" || - runtime.GOOS == "linux" || - runtime.GOOS == "netbsd" || - runtime.GOOS == "openbsd" || - runtime.GOOS == "solaris" { + case "aix", "dragonfly", "freebsd", "hurd", "illumos", "linux", "netbsd", "openbsd", "solaris": return platformUnix - } - - if runtime.GOOS == "android" { + case "android": return platformAndroid - } - - if runtime.GOOS == "ios" { + case "ios": return platformIOS - } - - if runtime.GOOS == "darwin" { + case "darwin": return platformMacOS + default: + return platformUnknown } - - return platformUnknown } type mappingType int From 21a906bc82fd8494df6e3c9f9ad763cb065064db Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Thu, 18 Apr 2024 14:15:54 +0900 Subject: [PATCH 11/81] internal/gamepaddb: refactoring: GLFW gamepads are requried only for Windows Updates #2964 --- internal/gamepaddb/db_glfw.go | 33 ---------------------------- internal/gamepaddb/db_windows.go | 13 +++++++++++ internal/gamepaddb/gamepaddb_test.go | 15 +++++++++++++ internal/gamepaddb/gen.go | 24 ++++++++++++++++++-- 4 files changed, 50 insertions(+), 35 deletions(-) delete mode 100644 internal/gamepaddb/db_glfw.go diff --git a/internal/gamepaddb/db_glfw.go b/internal/gamepaddb/db_glfw.go deleted file mode 100644 index 4c0eb6095..000000000 --- a/internal/gamepaddb/db_glfw.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2024 The Ebitengine Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build !android && !ios && !js && !microsoftgdk && !nintendosdk && !playstation5 - -package gamepaddb - -var additionalGLFWGamepads = []byte(` -78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, -78696e70757402000000000000000000,XInput Wheel (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, -78696e70757403000000000000000000,XInput Arcade Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, -78696e70757404000000000000000000,XInput Flight Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, -78696e70757405000000000000000000,XInput Dance Pad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, -78696e70757406000000000000000000,XInput Guitar (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, -78696e70757408000000000000000000,XInput Drum Kit (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, -`) - -func init() { - if err := Update(additionalGLFWGamepads); err != nil { - panic(err) - } -} diff --git a/internal/gamepaddb/db_windows.go b/internal/gamepaddb/db_windows.go index 85acf7209..9fd126948 100644 --- a/internal/gamepaddb/db_windows.go +++ b/internal/gamepaddb/db_windows.go @@ -25,8 +25,21 @@ import ( //go:embed gamecontrollerdb_windows.txt var controllerBytes []byte +var additionalGLFWGamepads = []byte(` +78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, +78696e70757402000000000000000000,XInput Wheel (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, +78696e70757403000000000000000000,XInput Arcade Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, +78696e70757404000000000000000000,XInput Flight Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, +78696e70757405000000000000000000,XInput Dance Pad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, +78696e70757406000000000000000000,XInput Guitar (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, +78696e70757408000000000000000000,XInput Drum Kit (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, +`) + func init() { if err := Update(controllerBytes); err != nil { panic(err) } + if err := Update(additionalGLFWGamepads); err != nil { + panic(err) + } } diff --git a/internal/gamepaddb/gamepaddb_test.go b/internal/gamepaddb/gamepaddb_test.go index eb2fec887..0657e5e8f 100644 --- a/internal/gamepaddb/gamepaddb_test.go +++ b/internal/gamepaddb/gamepaddb_test.go @@ -15,6 +15,7 @@ package gamepaddb_test import ( + "runtime" "testing" "github.com/hajimehoshi/ebiten/v2/internal/gamepaddb" @@ -65,3 +66,17 @@ func TestUpdate(t *testing.T) { } } } + +func TestGLFWGamepadMappings(t *testing.T) { + if runtime.GOOS != "windows" { + t.Skip("the current platform doesn't use GLFW gamepad mappings") + } + + const id = "78696e70757401000000000000000000" + if got, want := gamepaddb.HasStandardLayoutMapping(id), true; got != want { + t.Errorf("got: %v, want: %v", got, want) + } + if got, want := gamepaddb.Name(id), "XInput Gamepad (GLFW)"; got != want { + t.Errorf("got: %q, want: %q", got, want) + } +} diff --git a/internal/gamepaddb/gen.go b/internal/gamepaddb/gen.go index 9a2c1c54e..befe2c823 100644 --- a/internal/gamepaddb/gen.go +++ b/internal/gamepaddb/gen.go @@ -66,11 +66,27 @@ import ( //go:embed gamecontrollerdb_{{.FileNameSuffix}}.txt var controllerBytes []byte +{{if .HasGLFWGamepads}} +var additionalGLFWGamepads = []byte(` + "`" + ` +78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, +78696e70757402000000000000000000,XInput Wheel (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, +78696e70757403000000000000000000,XInput Arcade Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, +78696e70757404000000000000000000,XInput Flight Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, +78696e70757405000000000000000000,XInput Dance Pad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, +78696e70757406000000000000000000,XInput Guitar (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, +78696e70757408000000000000000000,XInput Drum Kit (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8, +` + "`" + `) +{{end}} + func init() { if err := Update(controllerBytes); err != nil { panic(err) - } -}` + }{{if .HasGLFWGamepads}} + if err := Update(additionalGLFWGamepads); err != nil { + panic(err) + }{{end}} +} +` func main() { if err := run(); err != nil { @@ -86,12 +102,14 @@ func run() error { type gamePadPlatform struct { filenameSuffix string buildConstraints string + hasGLFWGamepads bool } platforms := map[string]gamePadPlatform{ "Windows": { filenameSuffix: "windows", buildConstraints: "//go:build !microsoftgdk", + hasGLFWGamepads: true, }, "Mac OS X": { filenameSuffix: "macos_darwin", @@ -142,11 +160,13 @@ func run() error { DoNotEdit string BuildConstraints string FileNameSuffix string + HasGLFWGamepads bool }{ License: license, DoNotEdit: doNotEdit, BuildConstraints: platform.buildConstraints, FileNameSuffix: platform.filenameSuffix, + HasGLFWGamepads: platform.hasGLFWGamepads, }); err != nil { return err } From bc9e9d8562101ddcf54c4eebc839ed2bd8442de5 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 20 Apr 2024 21:21:57 +0900 Subject: [PATCH 12/81] ebiten: move the builtin shader to internal/builtinshader Updates #2861 --- colorm/colorm.go | 2 +- gameforui.go | 26 ++----------------------- internal/atlas/shader.go | 4 ++-- internal/builtinshader/shader.go | 27 ++++++++++++++++++++++++-- internal/graphicscommand/image_test.go | 2 +- internal/shaderir/bench_test.go | 2 +- shader.go | 2 +- 7 files changed, 33 insertions(+), 32 deletions(-) diff --git a/colorm/colorm.go b/colorm/colorm.go index 5e6d0f2aa..a3e1f0bee 100644 --- a/colorm/colorm.go +++ b/colorm/colorm.go @@ -179,7 +179,7 @@ func builtinShader(filter builtinshader.Filter, address builtinshader.Address) * return s } - src := builtinshader.Shader(filter, address, true) + src := builtinshader.ShaderSource(filter, address, true) s, err := ebiten.NewShader(src) if err != nil { panic(fmt.Sprintf("colorm: NewShader for a built-in shader failed: %v", err)) diff --git a/gameforui.go b/gameforui.go index bff312f75..a8328c2e2 100644 --- a/gameforui.go +++ b/gameforui.go @@ -21,32 +21,10 @@ import ( "sync/atomic" "github.com/hajimehoshi/ebiten/v2/internal/atlas" + "github.com/hajimehoshi/ebiten/v2/internal/builtinshader" "github.com/hajimehoshi/ebiten/v2/internal/ui" ) -const screenShaderSrc = `//kage:unit pixels - -package main - -func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 { - // Blend source colors in a square region, which size is 1/scale. - scale := imageDstSize()/imageSrc0Size() - pos := srcPos - p0 := pos - 1/2.0/scale - p1 := pos + 1/2.0/scale - - // Texels must be in the source rect, so it is not necessary to check. - c0 := imageSrc0UnsafeAt(p0) - c1 := imageSrc0UnsafeAt(vec2(p1.x, p0.y)) - c2 := imageSrc0UnsafeAt(vec2(p0.x, p1.y)) - c3 := imageSrc0UnsafeAt(p1) - - // p is the p1 value in one pixel assuming that the pixel's upper-left is (0, 0) and the lower-right is (1, 1). - rate := clamp(fract(p1)*scale, 0, 1) - return mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y) -} -` - var screenFilterEnabled = int32(1) func isScreenFilterEnabled() bool { @@ -76,7 +54,7 @@ func newGameForUI(game Game, transparent bool) *gameForUI { transparent: transparent, } - s, err := NewShader([]byte(screenShaderSrc)) + s, err := NewShader(builtinshader.ScreenShaderSource) if err != nil { panic(fmt.Sprintf("ebiten: compiling the screen shader failed: %v", err)) } diff --git a/internal/atlas/shader.go b/internal/atlas/shader.go index 0b91c0431..f90a5b0b7 100644 --- a/internal/atlas/shader.go +++ b/internal/atlas/shader.go @@ -88,7 +88,7 @@ var ( func init() { var wg errgroup.Group wg.Go(func() error { - ir, err := graphics.CompileShader([]byte(builtinshader.Shader(builtinshader.FilterNearest, builtinshader.AddressUnsafe, false))) + ir, err := graphics.CompileShader([]byte(builtinshader.ShaderSource(builtinshader.FilterNearest, builtinshader.AddressUnsafe, false))) if err != nil { return fmt.Errorf("atlas: compiling the nearest shader failed: %w", err) } @@ -96,7 +96,7 @@ func init() { return nil }) wg.Go(func() error { - ir, err := graphics.CompileShader([]byte(builtinshader.Shader(builtinshader.FilterLinear, builtinshader.AddressUnsafe, false))) + ir, err := graphics.CompileShader([]byte(builtinshader.ShaderSource(builtinshader.FilterLinear, builtinshader.AddressUnsafe, false))) if err != nil { return fmt.Errorf("atlas: compiling the linear shader failed: %w", err) } diff --git a/internal/builtinshader/shader.go b/internal/builtinshader/shader.go index ab48f6c29..e5a60abfe 100644 --- a/internal/builtinshader/shader.go +++ b/internal/builtinshader/shader.go @@ -123,10 +123,10 @@ func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 { `)) -// Shader returns the built-in shader based on the given parameters. +// ShaderSource returns the built-in shader source based on the given parameters. // // The returned shader always uses a color matrix so far. -func Shader(filter Filter, address Address, useColorM bool) []byte { +func ShaderSource(filter Filter, address Address, useColorM bool) []byte { shadersM.Lock() defer shadersM.Unlock() @@ -165,3 +165,26 @@ func Shader(filter Filter, address Address, useColorM bool) []byte { shaders[filter][address][c] = b return b } + +var ScreenShaderSource = []byte(`//kage:unit pixels + +package main + +func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 { + // Blend source colors in a square region, which size is 1/scale. + scale := imageDstSize()/imageSrc0Size() + pos := srcPos + p0 := pos - 1/2.0/scale + p1 := pos + 1/2.0/scale + + // Texels must be in the source rect, so it is not necessary to check. + c0 := imageSrc0UnsafeAt(p0) + c1 := imageSrc0UnsafeAt(vec2(p1.x, p0.y)) + c2 := imageSrc0UnsafeAt(vec2(p0.x, p1.y)) + c3 := imageSrc0UnsafeAt(p1) + + // p is the p1 value in one pixel assuming that the pixel's upper-left is (0, 0) and the lower-right is (1, 1). + rate := clamp(fract(p1)*scale, 0, 1) + return mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y) +} +`) diff --git a/internal/graphicscommand/image_test.go b/internal/graphicscommand/image_test.go index e8d08e032..5a00e0fed 100644 --- a/internal/graphicscommand/image_test.go +++ b/internal/graphicscommand/image_test.go @@ -31,7 +31,7 @@ import ( var nearestFilterShader *graphicscommand.Shader func init() { - ir, err := graphics.CompileShader([]byte(builtinshader.Shader(builtinshader.FilterNearest, builtinshader.AddressUnsafe, false))) + ir, err := graphics.CompileShader([]byte(builtinshader.ShaderSource(builtinshader.FilterNearest, builtinshader.AddressUnsafe, false))) if err != nil { panic(fmt.Sprintf("graphicscommand: compiling the nearest shader failed: %v", err)) } diff --git a/internal/shaderir/bench_test.go b/internal/shaderir/bench_test.go index 07102934c..ad7fabc8b 100644 --- a/internal/shaderir/bench_test.go +++ b/internal/shaderir/bench_test.go @@ -22,7 +22,7 @@ import ( ) func BenchmarkFilter(b *testing.B) { - src := builtinshader.Shader(builtinshader.FilterNearest, builtinshader.AddressUnsafe, false) + src := builtinshader.ShaderSource(builtinshader.FilterNearest, builtinshader.AddressUnsafe, false) s, err := graphics.CompileShader(src) if err != nil { b.Fatal(err) diff --git a/shader.go b/shader.go index 762289bc6..7ae669da4 100644 --- a/shader.go +++ b/shader.go @@ -107,7 +107,7 @@ func builtinShader(filter builtinshader.Filter, address builtinshader.Address, u shader = &Shader{shader: ui.LinearFilterShader} } } else { - src := builtinshader.Shader(filter, address, useColorM) + src := builtinshader.ShaderSource(filter, address, useColorM) s, err := NewShader(src) if err != nil { panic(fmt.Sprintf("ebiten: NewShader for a built-in shader failed: %v", err)) From bf7acd54bb310745de030298bbc6223ea7af8208 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 21 Apr 2024 22:46:51 +0900 Subject: [PATCH 13/81] text/v2: refactoring --- text/v2/gotextfacesource.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/text/v2/gotextfacesource.go b/text/v2/gotextfacesource.go index 65e3d68b4..3e84adde6 100644 --- a/text/v2/gotextfacesource.go +++ b/text/v2/gotextfacesource.go @@ -178,14 +178,16 @@ func (g *GoTextFaceSource) shape(text string, face *GoTextFace) ([]shaping.Outpu return out.outputs, out.glyphs } - g.f.SetVariations(face.variations) + f := face.Source.f + f.SetVariations(face.variations) + runes := []rune(text) input := shaping.Input{ Text: runes, RunStart: 0, RunEnd: len(runes), Direction: face.diDirection(), - Face: face.Source.f, + Face: f, FontFeatures: face.features, Size: float64ToFixed26_6(face.Size), Script: face.gScript(), @@ -193,7 +195,7 @@ func (g *GoTextFaceSource) shape(text string, face *GoTextFace) ([]shaping.Outpu } var seg shaping.Segmenter - inputs := seg.Split(input, &singleFontmap{face: face.Source.f}) + inputs := seg.Split(input, &singleFontmap{face: f}) if face.Direction == DirectionRightToLeft { // Reverse the input for RTL texts. @@ -222,7 +224,7 @@ func (g *GoTextFaceSource) shape(text string, face *GoTextFace) ([]shaping.Outpu switch data := g.f.GlyphData(gl.GlyphID).(type) { case api.GlyphOutline: if out.Direction.IsSideways() { - data.Sideways(fixed26_6ToFloat32(-gl.YOffset) / fixed26_6ToFloat32(out.Size) * float32(face.Source.f.Upem())) + data.Sideways(fixed26_6ToFloat32(-gl.YOffset) / fixed26_6ToFloat32(out.Size) * float32(f.Upem())) } segs = data.Segments case api.GlyphSVG: From af4638d83f6a4714d8e637ec4545fc58ce5ceb7a Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Wed, 24 Apr 2024 00:56:11 +0900 Subject: [PATCH 14/81] all: update bitmapfont to v3.1.0 --- go.mod | 2 +- go.sum | 17 +++++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index a45785ef2..9170fc2cb 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/ebitengine/purego v0.8.0-alpha.1 github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f github.com/go-text/typesetting v0.1.1-0.20240402181327-ced1d6822703 - github.com/hajimehoshi/bitmapfont/v3 v3.0.0 + github.com/hajimehoshi/bitmapfont/v3 v3.1.0 github.com/hajimehoshi/go-mp3 v0.3.4 github.com/jakecoffman/cp v1.2.1 github.com/jezek/xgb v1.1.1 diff --git a/go.sum b/go.sum index f3d81e503..3ade37650 100644 --- a/go.sum +++ b/go.sum @@ -11,8 +11,8 @@ github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f/go.mod h1:i/ebyRR github.com/go-text/typesetting v0.1.1-0.20240402181327-ced1d6822703 h1:AqtMl9yw7r319Ah4W2afQm3Ql+PEsQKHds18tGvKhog= github.com/go-text/typesetting v0.1.1-0.20240402181327-ced1d6822703/go.mod h1:2+owI/sxa73XA581LAzVuEBZ3WEEV2pXeDswCH/3i1I= github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66 h1:GUrm65PQPlhFSKjLPGOZNPNxLCybjzjYBzjfoBGaDUY= -github.com/hajimehoshi/bitmapfont/v3 v3.0.0 h1:r2+6gYK38nfztS/et50gHAswb9hXgxXECYgE8Nczmi4= -github.com/hajimehoshi/bitmapfont/v3 v3.0.0/go.mod h1:+CxxG+uMmgU4mI2poq944i3uZ6UYFfAkj9V6WqmuvZA= +github.com/hajimehoshi/bitmapfont/v3 v3.1.0 h1:JLy/na2e83GewqebpFbS2LHpDVnGdzmyJOpqXtBgLm0= +github.com/hajimehoshi/bitmapfont/v3 v3.1.0/go.mod h1:VVaVK/4HpV1MHWswCl5miFOuLoRVyIplB3qEJxZK2OA= github.com/hajimehoshi/go-mp3 v0.3.4 h1:NUP7pBYH8OguP4diaTZ9wJbUbk3tC0KlfzsEpWmYj68= github.com/hajimehoshi/go-mp3 v0.3.4/go.mod h1:fRtZraRFcWb0pu7ok0LqyFhCUrPeMsGRSVop0eemFmo= github.com/hajimehoshi/oto/v2 v2.3.1/go.mod h1:seWLbgHH7AyUMYKfKYT9pg7PhUu9/SisyJvNTT+ASQo= @@ -26,43 +26,48 @@ github.com/jfreymuth/vorbis v1.0.2 h1:m1xH6+ZI4thH927pgKD8JOH4eaGRm18rEE9/0WKjvN github.com/jfreymuth/vorbis v1.0.2/go.mod h1:DoftRo4AznKnShRl1GxiTFCseHr4zR9BN3TWXyuzrqQ= github.com/kisielk/errcheck v1.7.0 h1:+SbscKmWJ5mOK/bO1zS60F5I9WwZDWOfRsC4RwfwRV0= github.com/kisielk/errcheck v1.7.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/FqKluHJQ= -github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/image v0.1.0/go.mod h1:iyPr49SD/G/TBxYVB/9RRtGUT5eNbo2u4NamWeQcD5c= golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8= golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From f43c237c00e36e7a0c37e3c3713334129d6ac857 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 28 Apr 2024 20:20:35 +0900 Subject: [PATCH 15/81] .github/workflows: bug fix: Wasm tests didn't work on macOS and Windows Closes #2973 --- .github/workflows/test.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 74c51fd85..910f27761 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,9 +44,9 @@ jobs: - name: Install wasmbrowsertest run: | - go install github.com/agnivade/wasmbrowsertest@bb77d443e302d06f0d2462336bc2765942e0e862 + go install github.com/agnivade/wasmbrowsertest@ee76d31b7b9b1645576c1f51fec4c09fe6cf1bb3 mv $(go env GOPATH)/bin/wasmbrowsertest${{ runner.os == 'Windows' && '.exe' || '' }} $(go env GOPATH)/bin/go_js_wasm_exec${{ runner.os == 'Windows' && '.exe' || '' }} - go install github.com/agnivade/wasmbrowsertest/cmd/cleanenv@bb77d443e302d06f0d2462336bc2765942e0e862 + go install github.com/agnivade/wasmbrowsertest/cmd/cleanenv@ee76d31b7b9b1645576c1f51fec4c09fe6cf1bb3 - name: Prepare ebitenmobile test run: | @@ -169,8 +169,10 @@ jobs: env GOARCH=386 EBITENGINE_DIRECTX=version=12 go test -shuffle=on -v ./... - name: go test (Wasm) + if: runner.os != 'macOS' run: | - env GOOS=js GOARCH=wasm cleanenv -remove-prefix GITHUB_ -remove-prefix JAVA_ -- go test -shuffle=on -v ./... + # Wasm tests don't work on macOS with the headless mode, and the headless mode doesn't work in GitHub Actions (#2973). + env GOOS=js GOARCH=wasm cleanenv -remove-prefix GITHUB_ -remove-prefix JAVA_ -remove-prefix PSModulePath -remove-prefix STATS_ -remove-prefix RUNNER_ -- go test -shuffle=on -v ./... - name: Install ebitenmobile run: | From 97a9ee960101db8176d37bdd63d7d006553fd10c Mon Sep 17 00:00:00 2001 From: guangwu Date: Mon, 29 Apr 2024 08:43:11 +0800 Subject: [PATCH 16/81] ebitenutil: close an HTTP response body (#2971) Signed-off-by: guoguangwu --- ebitenutil/file_js.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ebitenutil/file_js.go b/ebitenutil/file_js.go index 9c6be7eb4..d477eb795 100644 --- a/ebitenutil/file_js.go +++ b/ebitenutil/file_js.go @@ -40,6 +40,7 @@ func OpenFile(path string) (ReadSeekCloser, error) { if err != nil { return nil, err } + defer res.Body.Close() body, err := io.ReadAll(res.Body) if err != nil { return nil, err From 13d15b0ed9a2b7d4a4821c02171e4269143aabec Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 29 Apr 2024 09:57:54 +0900 Subject: [PATCH 17/81] .github/workflows: fix comments Updates #2972 --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 910f27761..b94c6c33d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -171,7 +171,7 @@ jobs: - name: go test (Wasm) if: runner.os != 'macOS' run: | - # Wasm tests don't work on macOS with the headless mode, and the headless mode doesn't work in GitHub Actions (#2973). + # Wasm tests don't work on macOS with the headless mode, and the headless mode doesn't work in GitHub Actions (#2972). env GOOS=js GOARCH=wasm cleanenv -remove-prefix GITHUB_ -remove-prefix JAVA_ -remove-prefix PSModulePath -remove-prefix STATS_ -remove-prefix RUNNER_ -- go test -shuffle=on -v ./... - name: Install ebitenmobile From c390f0a9fa0aad0516abbea980a99f7463ed4821 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 29 Apr 2024 16:44:31 +0900 Subject: [PATCH 18/81] internal/shader: bug fix: treat a built-in function as an invalid argument Closes #2965 --- internal/shader/expr.go | 2 +- internal/shader/syntax_test.go | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/internal/shader/expr.go b/internal/shader/expr.go index 1c063820f..f5460a831 100644 --- a/internal/shader/expr.go +++ b/internal/shader/expr.go @@ -199,7 +199,7 @@ func (cs *compileState) parseExpr(block *block, fname string, expr ast.Expr, mar return nil, nil, nil, false } for _, expr := range es { - if expr.Type == shaderir.FunctionExpr { + if expr.Type == shaderir.FunctionExpr || expr.Type == shaderir.BuiltinFuncExpr { cs.addError(e.Pos(), fmt.Sprintf("function name cannot be an argument: %s", e.Fun)) return nil, nil, nil, false } diff --git a/internal/shader/syntax_test.go b/internal/shader/syntax_test.go index 61512ee2e..3c07065b2 100644 --- a/internal/shader/syntax_test.go +++ b/internal/shader/syntax_test.go @@ -1897,6 +1897,16 @@ func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 { Foo(Bar()) return color } +`)); err == nil { + t.Errorf("error must be non-nil but was nil") + } + // Issue #2965 + if _, err := compileToIR([]byte(`package main + +func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 { + abs(sign) + return color +} `)); err == nil { t.Errorf("error must be non-nil but was nil") } From 359b5abb621adf22a34e6671f415ea8efe93b471 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 29 Apr 2024 18:45:28 +0900 Subject: [PATCH 19/81] all: update dependencies --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 9170fc2cb..1d122038e 100644 --- a/go.mod +++ b/go.mod @@ -16,13 +16,13 @@ require ( github.com/jfreymuth/oggvorbis v1.0.5 github.com/kisielk/errcheck v1.7.0 golang.org/x/image v0.15.0 - golang.org/x/sync v0.6.0 - golang.org/x/sys v0.18.0 + golang.org/x/sync v0.7.0 + golang.org/x/sys v0.19.0 golang.org/x/text v0.14.0 - golang.org/x/tools v0.19.0 + golang.org/x/tools v0.20.0 ) require ( github.com/jfreymuth/vorbis v1.0.2 // indirect - golang.org/x/mod v0.16.0 // indirect + golang.org/x/mod v0.17.0 // indirect ) diff --git a/go.sum b/go.sum index 3ade37650..55d3ddcb5 100644 --- a/go.sum +++ b/go.sum @@ -34,8 +34,8 @@ golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8= golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= -golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -43,8 +43,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -53,8 +53,8 @@ golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -68,6 +68,6 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= -golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= +golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= +golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 9c374a958e5f8ff13b6ad878e73310973e869448 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 29 Apr 2024 18:52:14 +0900 Subject: [PATCH 20/81] all: update gomobile --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1d122038e..1303909b7 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/hajimehoshi/ebiten/v2 go 1.18 require ( - github.com/ebitengine/gomobile v0.0.0-20240329170434-1771503ff0a8 + github.com/ebitengine/gomobile v0.0.0-20240429094902-cf88669c3591 github.com/ebitengine/hideconsole v1.0.0 github.com/ebitengine/oto/v3 v3.3.0-alpha.1 github.com/ebitengine/purego v0.8.0-alpha.1 diff --git a/go.sum b/go.sum index 55d3ddcb5..6e729c045 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/ebitengine/gomobile v0.0.0-20240329170434-1771503ff0a8 h1:5e8X7WEdOWrjrKvgaWF6PRnDvJicfrkEnwAkWtMN74g= -github.com/ebitengine/gomobile v0.0.0-20240329170434-1771503ff0a8/go.mod h1:tWboRRNagZwwwis4QIgEFG1ZNFwBJ3LAhSLAXAAxobQ= +github.com/ebitengine/gomobile v0.0.0-20240429094902-cf88669c3591 h1:iCauZ8Q3KW2EHpj+Re4qRU4/Y7ustOTpNV6m8hx5BqE= +github.com/ebitengine/gomobile v0.0.0-20240429094902-cf88669c3591/go.mod h1:7zsHIJi2c+Jo+wIL9wD+ADWzMbtG8jhUQpplXIrZ7EI= github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= github.com/ebitengine/oto/v3 v3.3.0-alpha.1 h1:J2nBmQwPLKc4+yLObytq1jKNydI96l6EjZfgefiqGbk= From f2a18ed6babc203211fc6d378783a95d22cf7b59 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 29 Apr 2024 18:56:52 +0900 Subject: [PATCH 21/81] all: bug fix: upgrade the Go version to 1.19 golang.org/x/tools v0.20.0 no longer supports Go 1.18. --- .github/workflows/test.yml | 2 +- go.mod | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b94c6c33d..5e15a2ef0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,7 +7,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - go: ['1.18.x', '1.19.x', '1.20.x', '1.21.x', '1.22.x'] + go: ['1.19.x', '1.20.x', '1.21.x', '1.22.x'] name: Test with Go ${{ matrix.go }} on ${{ matrix.os }} runs-on: ${{ matrix.os }} env: diff --git a/go.mod b/go.mod index 1303909b7..05ec89b12 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/hajimehoshi/ebiten/v2 -go 1.18 +go 1.19 require ( github.com/ebitengine/gomobile v0.0.0-20240429094902-cf88669c3591 From f34932151ddb8c12a9e5ea4fadea5ab12f424e2a Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 29 Apr 2024 19:02:23 +0900 Subject: [PATCH 22/81] all: use atomic.Bool instead of atomic.Store/LoadUint32 Updates #2422 --- gameforui.go | 16 +++------ internal/gamepad/gamepad_desktop_windows.go | 8 ++--- internal/graphicscommand/commandqueue.go | 14 ++++---- internal/ui/monitor_glfw.go | 8 ++--- internal/ui/ui.go | 30 ++++++---------- internal/ui/ui_mobile.go | 12 +++---- run.go | 38 +++++++-------------- window.go | 6 ++-- 8 files changed, 50 insertions(+), 82 deletions(-) diff --git a/gameforui.go b/gameforui.go index a8328c2e2..d86e4fda8 100644 --- a/gameforui.go +++ b/gameforui.go @@ -25,18 +25,10 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/ui" ) -var screenFilterEnabled = int32(1) +var screenFilterEnabled atomic.Bool -func isScreenFilterEnabled() bool { - return atomic.LoadInt32(&screenFilterEnabled) != 0 -} - -func setScreenFilterEnabled(enabled bool) { - v := int32(0) - if enabled { - v = 1 - } - atomic.StoreInt32(&screenFilterEnabled, v) +func init() { + screenFilterEnabled.Store(true) } type gameForUI struct { @@ -145,7 +137,7 @@ func (g *gameForUI) DrawFinalScreen(scale, offsetX, offsetY float64) { } switch { - case !isScreenFilterEnabled(), math.Floor(scale) == scale: + case !screenFilterEnabled.Load(), math.Floor(scale) == scale: op := &DrawImageOptions{} op.GeoM = geoM g.screen.DrawImage(g.offscreen, op) diff --git a/internal/gamepad/gamepad_desktop_windows.go b/internal/gamepad/gamepad_desktop_windows.go index 4ee5e8d06..024b99d88 100644 --- a/internal/gamepad/gamepad_desktop_windows.go +++ b/internal/gamepad/gamepad_desktop_windows.go @@ -113,7 +113,7 @@ type nativeGamepadsDesktop struct { enumObjectsCallback uintptr nativeWindow windows.HWND - deviceChanged int32 + deviceChanged atomic.Bool err error } @@ -537,11 +537,11 @@ func (g *nativeGamepadsDesktop) update(gamepads *gamepads) error { g.origWndProc = h } - if atomic.LoadInt32(&g.deviceChanged) != 0 { + if g.deviceChanged.Load() { if err := g.detectConnection(gamepads); err != nil { g.err = err } - atomic.StoreInt32(&g.deviceChanged, 0) + g.deviceChanged.Store(false) } return nil @@ -550,7 +550,7 @@ func (g *nativeGamepadsDesktop) update(gamepads *gamepads) error { func (g *nativeGamepadsDesktop) wndProc(hWnd uintptr, uMsg uint32, wParam, lParam uintptr) uintptr { switch uMsg { case _WM_DEVICECHANGE: - atomic.StoreInt32(&g.deviceChanged, 1) + g.deviceChanged.Store(true) } return _CallWindowProcW(g.origWndProc, hWnd, uMsg, wParam, lParam) } diff --git a/internal/graphicscommand/commandqueue.go b/internal/graphicscommand/commandqueue.go index f91a1a52b..0a57d69d2 100644 --- a/internal/graphicscommand/commandqueue.go +++ b/internal/graphicscommand/commandqueue.go @@ -43,14 +43,14 @@ const ( maxVertexFloatCount = MaxVertexCount * graphics.VertexFloatCount ) -var vsyncEnabled int32 = 1 +var vsyncEnabled atomic.Bool + +func init() { + vsyncEnabled.Store(true) +} func SetVsyncEnabled(enabled bool, graphicsDriver graphicsdriver.Graphics) { - if enabled { - atomic.StoreInt32(&vsyncEnabled, 1) - } else { - atomic.StoreInt32(&vsyncEnabled, 0) - } + vsyncEnabled.Store(enabled) runOnRenderThread(func() { graphicsDriver.SetVsyncEnabled(enabled) @@ -185,7 +185,7 @@ func (q *commandQueue) Flush(graphicsDriver graphicsdriver.Graphics, endFrame bo var sync bool // Disable asynchronous rendering when vsync is on, as this causes a rendering delay (#2822). - if endFrame && atomic.LoadInt32(&vsyncEnabled) != 0 { + if endFrame && vsyncEnabled.Load() { sync = true } if !sync { diff --git a/internal/ui/monitor_glfw.go b/internal/ui/monitor_glfw.go index aa12765cb..d491e993b 100644 --- a/internal/ui/monitor_glfw.go +++ b/internal/ui/monitor_glfw.go @@ -65,13 +65,13 @@ type monitors struct { m sync.Mutex - updateCalled int32 + updateCalled atomic.Bool } var theMonitors monitors func (m *monitors) append(ms []*Monitor) []*Monitor { - if atomic.LoadInt32(&m.updateCalled) == 0 { + if !m.updateCalled.Load() { panic("ui: (*monitors).update must be called before (*monitors).append is called") } @@ -82,7 +82,7 @@ func (m *monitors) append(ms []*Monitor) []*Monitor { } func (m *monitors) primaryMonitor() *Monitor { - if atomic.LoadInt32(&m.updateCalled) == 0 { + if !m.updateCalled.Load() { panic("ui: (*monitors).update must be called before (*monitors).primaryMonitor is called") } @@ -179,6 +179,6 @@ func (m *monitors) update() error { m.monitors = newMonitors m.m.Unlock() - atomic.StoreInt32(&m.updateCalled, 1) + m.updateCalled.Store(true) return nil } diff --git a/internal/ui/ui.go b/internal/ui/ui.go index 5f1a9dca9..ae42c2e0e 100644 --- a/internal/ui/ui.go +++ b/internal/ui/ui.go @@ -75,10 +75,10 @@ type UserInterface struct { err error errM sync.Mutex - isScreenClearedEveryFrame int32 + isScreenClearedEveryFrame atomic.Bool graphicsLibrary int32 - running int32 - terminated int32 + running atomic.Bool + terminated atomic.Bool whiteImage *Image @@ -107,9 +107,9 @@ func Get() *UserInterface { // newUserInterface must be called from the main thread. func newUserInterface() (*UserInterface, error) { u := &UserInterface{ - isScreenClearedEveryFrame: 1, - graphicsLibrary: int32(GraphicsLibraryUnknown), + graphicsLibrary: int32(GraphicsLibraryUnknown), } + u.isScreenClearedEveryFrame.Store(true) u.whiteImage = u.NewImage(3, 3, atlas.ImageTypeRegular) pix := make([]byte, 4*u.whiteImage.width*u.whiteImage.height) @@ -196,15 +196,11 @@ func (u *UserInterface) setError(err error) { } func (u *UserInterface) IsScreenClearedEveryFrame() bool { - return atomic.LoadInt32(&u.isScreenClearedEveryFrame) != 0 + return u.isScreenClearedEveryFrame.Load() } func (u *UserInterface) SetScreenClearedEveryFrame(cleared bool) { - v := int32(0) - if cleared { - v = 1 - } - atomic.StoreInt32(&u.isScreenClearedEveryFrame, v) + u.isScreenClearedEveryFrame.Store(cleared) } func (u *UserInterface) setGraphicsLibrary(library GraphicsLibrary) { @@ -216,21 +212,17 @@ func (u *UserInterface) GraphicsLibrary() GraphicsLibrary { } func (u *UserInterface) isRunning() bool { - return atomic.LoadInt32(&u.running) != 0 && !u.isTerminated() + return u.running.Load() && !u.isTerminated() } func (u *UserInterface) setRunning(running bool) { - if running { - atomic.StoreInt32(&u.running, 1) - } else { - atomic.StoreInt32(&u.running, 0) - } + u.running.Store(running) } func (u *UserInterface) isTerminated() bool { - return atomic.LoadInt32(&u.terminated) != 0 + return u.terminated.Load() } func (u *UserInterface) setTerminated() { - atomic.StoreInt32(&u.terminated, 1) + u.terminated.Store(true) } diff --git a/internal/ui/ui_mobile.go b/internal/ui/ui_mobile.go index d95ece36c..613471611 100644 --- a/internal/ui/ui_mobile.go +++ b/internal/ui/ui_mobile.go @@ -40,7 +40,6 @@ var ( func (u *UserInterface) init() error { u.userInterfaceImpl = userInterfaceImpl{ - foreground: 1, graphicsLibraryInitCh: make(chan struct{}), errCh: make(chan error), @@ -48,6 +47,7 @@ func (u *UserInterface) init() error { outsideWidth: 640, outsideHeight: 480, } + u.foreground.Store(true) return nil } @@ -89,7 +89,7 @@ type userInterfaceImpl struct { outsideWidth float64 outsideHeight float64 - foreground int32 + foreground atomic.Bool errCh chan error context *context @@ -104,11 +104,7 @@ type userInterfaceImpl struct { } func (u *UserInterface) SetForeground(foreground bool) error { - var v int32 - if foreground { - v = 1 - } - atomic.StoreInt32(&u.foreground, v) + u.foreground.Store(foreground) if foreground { return hook.ResumeAudio() @@ -220,7 +216,7 @@ func (u *UserInterface) SetFullscreen(fullscreen bool) { } func (u *UserInterface) IsFocused() bool { - return atomic.LoadInt32(&u.foreground) != 0 + return u.foreground.Load() } func (u *UserInterface) IsRunnableOnUnfocused() bool { diff --git a/run.go b/run.go index fb49bc222..683795da8 100644 --- a/run.go +++ b/run.go @@ -149,7 +149,7 @@ func CurrentFPS() float64 { } var ( - isRunGameEnded_ = int32(0) + isRunGameEnded_ atomic.Bool ) // SetScreenClearedEveryFrame enables or disables the clearing of the screen at the beginning of each frame. @@ -179,7 +179,7 @@ func IsScreenClearedEveryFrame() bool { // // Deprecated: as of v2.5. Use FinalScreenDrawer instead. func SetScreenFilterEnabled(enabled bool) { - setScreenFilterEnabled(enabled) + screenFilterEnabled.Store(enabled) } // IsScreenFilterEnabled returns true if Ebitengine's "screen" filter is enabled. @@ -188,7 +188,7 @@ func SetScreenFilterEnabled(enabled bool) { // // Deprecated: as of v2.5. func IsScreenFilterEnabled() bool { - return isScreenFilterEnabled() + return screenFilterEnabled.Load() } // Termination is a special error which indicates Game termination without error. @@ -309,17 +309,13 @@ type RunGameOptions struct { // // Don't call RunGame or RunGameWithOptions twice or more in one process. func RunGameWithOptions(game Game, options *RunGameOptions) error { - defer atomic.StoreInt32(&isRunGameEnded_, 1) + defer isRunGameEnded_.Store(true) initializeWindowPositionIfNeeded(WindowSize()) op := toUIRunOptions(options) // This is necessary to change the result of IsScreenTransparent. - if op.ScreenTransparent { - atomic.StoreInt32(&screenTransparent, 1) - } else { - atomic.StoreInt32(&screenTransparent, 0) - } + screenTransparent.Store(op.ScreenTransparent) g := newGameForUI(game, op.ScreenTransparent) if err := ui.Get().Run(g, op); err != nil { @@ -333,7 +329,7 @@ func RunGameWithOptions(game Game, options *RunGameOptions) error { } func isRunGameEnded() bool { - return atomic.LoadInt32(&isRunGameEnded_) != 0 + return isRunGameEnded_.Load() } // ScreenSizeInFullscreen returns the size in device-independent pixels when the game is fullscreen. @@ -640,7 +636,7 @@ func IsScreenTransparent() bool { if !ui.IsScreenTransparentAvailable() { return false } - return atomic.LoadInt32(&screenTransparent) != 0 + return screenTransparent.Load() } // SetScreenTransparent sets the state if the window is transparent. @@ -653,14 +649,10 @@ func IsScreenTransparent() bool { // // Deprecated: as of v2.5. Use RunGameWithOptions instead. func SetScreenTransparent(transparent bool) { - if transparent { - atomic.StoreInt32(&screenTransparent, 1) - } else { - atomic.StoreInt32(&screenTransparent, 0) - } + screenTransparent.Store(transparent) } -var screenTransparent int32 = 0 +var screenTransparent atomic.Bool // SetInitFocused sets whether the application is focused on show. // The default value is true, i.e., the application is focused. @@ -673,14 +665,10 @@ var screenTransparent int32 = 0 // // Deprecated: as of v2.5. Use RunGameWithOptions instead. func SetInitFocused(focused bool) { - if focused { - atomic.StoreInt32(&initUnfocused, 0) - } else { - atomic.StoreInt32(&initUnfocused, 1) - } + initUnfocused.Store(!focused) } -var initUnfocused int32 = 0 +var initUnfocused atomic.Bool func toUIRunOptions(options *RunGameOptions) *ui.RunOptions { const ( @@ -690,8 +678,8 @@ func toUIRunOptions(options *RunGameOptions) *ui.RunOptions { if options == nil { return &ui.RunOptions{ - InitUnfocused: atomic.LoadInt32(&initUnfocused) != 0, - ScreenTransparent: atomic.LoadInt32(&screenTransparent) != 0, + InitUnfocused: initUnfocused.Load(), + ScreenTransparent: screenTransparent.Load(), X11ClassName: defaultX11ClassName, X11InstanceName: defaultX11InstanceName, } diff --git a/window.go b/window.go index b5945fdcb..399785415 100644 --- a/window.go +++ b/window.go @@ -155,16 +155,16 @@ func WindowPosition() (x, y int) { // // SetWindowPosition is concurrent-safe. func SetWindowPosition(x, y int) { - atomic.StoreUint32(&windowPositionSetExplicitly, 1) + windowPositionSetExplicitly.Store(true) ui.Get().Window().SetPosition(x, y) } var ( - windowPositionSetExplicitly uint32 + windowPositionSetExplicitly atomic.Bool ) func initializeWindowPositionIfNeeded(width, height int) { - if atomic.LoadUint32(&windowPositionSetExplicitly) == 0 { + if !windowPositionSetExplicitly.Load() { sw, sh := ui.Get().Monitor().Size() x, y := ui.InitialWindowPosition(sw, sh, width, height) ui.Get().Window().SetPosition(x, y) From 59fb259181334b650c372f79494e0407652f9807 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 7 Apr 2024 02:04:02 +0900 Subject: [PATCH 23/81] internal/graphicsdriver/gl: use PureGo for Linux and UNIX Updates #2284 --- go.mod | 2 +- go.sum | 4 +- .../graphicsdriver/opengl/gl/default_cgo.go | 2 +- .../opengl/gl/default_purego.go | 2 +- .../opengl/gl/procaddr_linbsd.go | 73 ++++++++----------- 5 files changed, 36 insertions(+), 47 deletions(-) diff --git a/go.mod b/go.mod index 05ec89b12..934698a3a 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/ebitengine/gomobile v0.0.0-20240429094902-cf88669c3591 github.com/ebitengine/hideconsole v1.0.0 github.com/ebitengine/oto/v3 v3.3.0-alpha.1 - github.com/ebitengine/purego v0.8.0-alpha.1 + github.com/ebitengine/purego v0.8.0-alpha.1.0.20240429114929-1ff8716e2490 github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f github.com/go-text/typesetting v0.1.1-0.20240402181327-ced1d6822703 github.com/hajimehoshi/bitmapfont/v3 v3.1.0 diff --git a/go.sum b/go.sum index 6e729c045..343b20257 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= github.com/ebitengine/oto/v3 v3.3.0-alpha.1 h1:J2nBmQwPLKc4+yLObytq1jKNydI96l6EjZfgefiqGbk= github.com/ebitengine/oto/v3 v3.3.0-alpha.1/go.mod h1:T2/VV0UWG97GEEf4kORMU2nCneYT/YmwSTxPutSVaUg= -github.com/ebitengine/purego v0.8.0-alpha.1 h1:52AgJTNaQRi7YtOtdJl4hkxNWhAGMxuDuDjOVIp5Ojk= -github.com/ebitengine/purego v0.8.0-alpha.1/go.mod h1:y8L+ZRLphbdPW2xs41fur/KaW57yTzrFsqsclHyHrTM= +github.com/ebitengine/purego v0.8.0-alpha.1.0.20240429114929-1ff8716e2490 h1:hOHVTszt219H/6Ma0o/RvZ++BPJtj2PMie+3fyA+sTs= +github.com/ebitengine/purego v0.8.0-alpha.1.0.20240429114929-1ff8716e2490/go.mod h1:y8L+ZRLphbdPW2xs41fur/KaW57yTzrFsqsclHyHrTM= github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f h1:ysqRe+lvUiL0dH5XzkH0Bz68bFMPJ4f5Si4L/HD9SGk= github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f/go.mod h1:i/ebyRRv/IoHixuZ9bElZnXbmfoUVPGQpdsJ4sVuX38= github.com/go-text/typesetting v0.1.1-0.20240402181327-ced1d6822703 h1:AqtMl9yw7r319Ah4W2afQm3Ql+PEsQKHds18tGvKhog= diff --git a/internal/graphicsdriver/opengl/gl/default_cgo.go b/internal/graphicsdriver/opengl/gl/default_cgo.go index c9f5a58aa..535f469db 100644 --- a/internal/graphicsdriver/opengl/gl/default_cgo.go +++ b/internal/graphicsdriver/opengl/gl/default_cgo.go @@ -2,7 +2,7 @@ // SPDX-FileCopyrightText: 2014 Eric Woroshow // SPDX-FileCopyrightText: 2022 The Ebitengine Authors -//go:build !darwin && !js && !windows && !playstation5 +//go:build nintendosdk package gl diff --git a/internal/graphicsdriver/opengl/gl/default_purego.go b/internal/graphicsdriver/opengl/gl/default_purego.go index 179cd8f4e..dda9e1548 100644 --- a/internal/graphicsdriver/opengl/gl/default_purego.go +++ b/internal/graphicsdriver/opengl/gl/default_purego.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build darwin || windows +//go:build (darwin || freebsd || linux || netbsd || openbsd || windows) && !nintendosdk && !playstation5 package gl diff --git a/internal/graphicsdriver/opengl/gl/procaddr_linbsd.go b/internal/graphicsdriver/opengl/gl/procaddr_linbsd.go index a7b40dac4..63527eed4 100644 --- a/internal/graphicsdriver/opengl/gl/procaddr_linbsd.go +++ b/internal/graphicsdriver/opengl/gl/procaddr_linbsd.go @@ -16,38 +16,18 @@ package gl -// #cgo LDFLAGS: -ldl -// -// #include -// #include -// -// static void* getProcAddressGL(void* libGL, const char* name) { -// static void*(*glXGetProcAddress)(const char*); -// if (!glXGetProcAddress) { -// glXGetProcAddress = dlsym(libGL, "glXGetProcAddress"); -// if (!glXGetProcAddress) { -// glXGetProcAddress = dlsym(libGL, "glXGetProcAddressARB"); -// } -// } -// return glXGetProcAddress(name); -// } -// -// static void* getProcAddressGLES(void* libGLES, const char* name) { -// return dlsym(libGLES, name); -// } -import "C" - import ( "fmt" "os" "runtime" "strings" - "unsafe" + + "github.com/ebitengine/purego" ) var ( - libGL unsafe.Pointer - libGLES unsafe.Pointer + libGL uintptr + libGLES uintptr ) func (c *defaultContext) init() error { @@ -72,10 +52,8 @@ func (c *defaultContext) init() error { // [1] https://github.com/glfw/glfw/commit/55aad3c37b67f17279378db52da0a3ab81bbf26d // [2] https://github.com/glfw/glfw/commit/c18851f52ec9704eb06464058a600845ec1eada1 for _, name := range []string{"libGL.so", "libGL.so.2", "libGL.so.1", "libGL.so.0"} { - cname := C.CString(name) - lib := C.dlopen(cname, C.RTLD_LAZY|C.RTLD_GLOBAL) - C.free(unsafe.Pointer(cname)) - if lib != nil { + lib, err := purego.Dlopen(name, purego.RTLD_LAZY|purego.RTLD_GLOBAL) + if err == nil { libGL = lib return nil } @@ -84,10 +62,8 @@ func (c *defaultContext) init() error { // Try OpenGL ES. for _, name := range []string{"libGLESv2.so", "libGLESv2.so.2", "libGLESv2.so.1", "libGLESv2.so.0"} { - cname := C.CString(name) - lib := C.dlopen(cname, C.RTLD_LAZY|C.RTLD_GLOBAL) - C.free(unsafe.Pointer(cname)) - if lib != nil { + lib, err := purego.Dlopen(name, purego.RTLD_LAZY|purego.RTLD_GLOBAL) + if err == nil { libGLES = lib c.isES = true return nil @@ -99,19 +75,32 @@ func (c *defaultContext) init() error { func (c *defaultContext) getProcAddress(name string) (uintptr, error) { if c.isES { - return getProcAddressGLES(name), nil + return getProcAddressGLES(name) } - return getProcAddressGL(name), nil + return getProcAddressGL(name) } -func getProcAddressGL(name string) uintptr { - cname := C.CString(name) - defer C.free(unsafe.Pointer(cname)) - return uintptr(C.getProcAddressGL(libGL, cname)) +var glXGetProcAddress func(name string) uintptr + +func getProcAddressGL(name string) (uintptr, error) { + if glXGetProcAddress == nil { + if _, err := purego.Dlsym(libGL, "glXGetProcAddress"); err == nil { + purego.RegisterLibFunc(&glXGetProcAddress, libGL, "glXGetProcAddress") + } else if _, err := purego.Dlsym(libGL, "glXGetProcAddressARB"); err == nil { + purego.RegisterLibFunc(&glXGetProcAddress, libGL, "glXGetProcAddressARB") + } + } + if glXGetProcAddress == nil { + return 0, fmt.Errorf("gl: failed to find glXGetProcAddress or glXGetProcAddressARB in libGL.so") + } + + return glXGetProcAddress(name), nil } -func getProcAddressGLES(name string) uintptr { - cname := C.CString(name) - defer C.free(unsafe.Pointer(cname)) - return uintptr(C.getProcAddressGLES(libGLES, cname)) +func getProcAddressGLES(name string) (uintptr, error) { + proc, err := purego.Dlsym(libGLES, name) + if err != nil { + return 0, err + } + return proc, nil } From bb799da51f80f029011b9b63d5c43725e88a8363 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 29 Apr 2024 21:50:08 +0900 Subject: [PATCH 24/81] internal/ui: use atomic.Int32 instead of atomic.Store/LoadInt32 Updates #2422 --- internal/ui/ui.go | 11 +++++------ internal/ui/ui_mobile.go | 12 ++++++------ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/internal/ui/ui.go b/internal/ui/ui.go index ae42c2e0e..82c2ff546 100644 --- a/internal/ui/ui.go +++ b/internal/ui/ui.go @@ -76,7 +76,7 @@ type UserInterface struct { errM sync.Mutex isScreenClearedEveryFrame atomic.Bool - graphicsLibrary int32 + graphicsLibrary atomic.Int32 running atomic.Bool terminated atomic.Bool @@ -106,10 +106,9 @@ func Get() *UserInterface { // newUserInterface must be called from the main thread. func newUserInterface() (*UserInterface, error) { - u := &UserInterface{ - graphicsLibrary: int32(GraphicsLibraryUnknown), - } + u := &UserInterface{} u.isScreenClearedEveryFrame.Store(true) + u.graphicsLibrary.Store(int32(GraphicsLibraryUnknown)) u.whiteImage = u.NewImage(3, 3, atlas.ImageTypeRegular) pix := make([]byte, 4*u.whiteImage.width*u.whiteImage.height) @@ -204,11 +203,11 @@ func (u *UserInterface) SetScreenClearedEveryFrame(cleared bool) { } func (u *UserInterface) setGraphicsLibrary(library GraphicsLibrary) { - atomic.StoreInt32(&u.graphicsLibrary, int32(library)) + u.graphicsLibrary.Store(int32(library)) } func (u *UserInterface) GraphicsLibrary() GraphicsLibrary { - return GraphicsLibrary(atomic.LoadInt32(&u.graphicsLibrary)) + return GraphicsLibrary(u.graphicsLibrary.Load()) } func (u *UserInterface) isRunning() bool { diff --git a/internal/ui/ui_mobile.go b/internal/ui/ui_mobile.go index 613471611..b86e9a755 100644 --- a/internal/ui/ui_mobile.go +++ b/internal/ui/ui_mobile.go @@ -97,7 +97,7 @@ type userInterfaceImpl struct { inputState InputState touches []TouchForInput - fpsMode int32 + fpsMode atomic.Int32 renderRequester RenderRequester m sync.RWMutex @@ -228,11 +228,11 @@ func (u *UserInterface) SetRunnableOnUnfocused(runnableOnUnfocused bool) { } func (u *UserInterface) FPSMode() FPSModeType { - return FPSModeType(atomic.LoadInt32(&u.fpsMode)) + return FPSModeType(u.fpsMode.Load()) } func (u *UserInterface) SetFPSMode(mode FPSModeType) { - atomic.StoreInt32(&u.fpsMode, int32(mode)) + u.fpsMode.Store(int32(mode)) u.updateExplicitRenderingModeIfNeeded(mode) } @@ -294,7 +294,7 @@ func (u *UserInterface) Monitor() *Monitor { func (u *UserInterface) UpdateInput(keys map[Key]struct{}, runes []rune, touches []TouchForInput) { u.updateInputStateFromOutside(keys, runes, touches) - if FPSModeType(atomic.LoadInt32(&u.fpsMode)) == FPSModeVsyncOffMinimum { + if FPSModeType(u.fpsMode.Load()) == FPSModeVsyncOffMinimum { u.renderRequester.RequestRenderIfNeeded() } } @@ -306,11 +306,11 @@ type RenderRequester interface { func (u *UserInterface) SetRenderRequester(renderRequester RenderRequester) { u.renderRequester = renderRequester - u.updateExplicitRenderingModeIfNeeded(FPSModeType(atomic.LoadInt32(&u.fpsMode))) + u.updateExplicitRenderingModeIfNeeded(FPSModeType(u.fpsMode.Load())) } func (u *UserInterface) ScheduleFrame() { - if u.renderRequester != nil && FPSModeType(atomic.LoadInt32(&u.fpsMode)) == FPSModeVsyncOffMinimum { + if u.renderRequester != nil && FPSModeType(u.fpsMode.Load()) == FPSModeVsyncOffMinimum { u.renderRequester.RequestRenderIfNeeded() } } From 12876343ff7b078e3ccc9ec5f9e3004240bfdbc1 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 29 Apr 2024 22:16:25 +0900 Subject: [PATCH 25/81] .github/workflows: allow go-vet for Go 1.19 --- .github/workflows/test.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5e15a2ef0..97a1489ab 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -77,9 +77,6 @@ jobs: go list ./... | grep -v -x -F -f .github/workflows/govetblock_windows.txt | xargs go vet - name: go vet (vettool) - # Stop vettools for old Go versions. Apparently this is an issue in golang.org/x/tools (golang/go#62519) - # TODO: Update golang.org/x/tools and remove this restriction. - if: ${{ !startsWith(matrix.go, '1.18.') && !startsWith(matrix.go, '1.19.') }} run: | go install ./internal/vettools go vet -vettool=$(which vettools)${{ runner.os == 'Windows' && '.exe' || '' }} -v ./... From 0af5b41d4842fed5756cdda5911639ae7c530633 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 29 Apr 2024 23:34:37 +0900 Subject: [PATCH 26/81] internal/atlas: refactoring --- internal/atlas/export_test.go | 8 ++------ internal/atlas/image.go | 15 ++++----------- internal/atlas/image_test.go | 30 +++++++++++++++--------------- 3 files changed, 21 insertions(+), 32 deletions(-) diff --git a/internal/atlas/export_test.go b/internal/atlas/export_test.go index dc0a00bf5..eb8af636c 100644 --- a/internal/atlas/export_test.go +++ b/internal/atlas/export_test.go @@ -14,16 +14,12 @@ package atlas -import ( - "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" -) - const ( BaseCountToPutOnSourceBackend = baseCountToPutOnSourceBackend ) -func PutImagesOnSourceBackendForTesting(graphicsDriver graphicsdriver.Graphics) { - putImagesOnSourceBackend(graphicsDriver) +func PutImagesOnSourceBackendForTesting() { + putImagesOnSourceBackend() } var ( diff --git a/internal/atlas/image.go b/internal/atlas/image.go index 9a07d015e..defce3335 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -36,13 +36,6 @@ var ( maxSize = 0 ) -func max(a, b int) int { - if a > b { - return a - } - return b -} - func min(a, b int) int { if a < b { return a @@ -81,7 +74,7 @@ func flushDeferred() { // Actual time duration is increased in an exponential way for each usage as a rendering target. const baseCountToPutOnSourceBackend = 10 -func putImagesOnSourceBackend(graphicsDriver graphicsdriver.Graphics) { +func putImagesOnSourceBackend() { // The counter usedAsDestinationCount is updated at most once per frame (#2676). imagesUsedAsDestination.forEach(func(i *Image) { // This counter is not updated when the backend is created in this frame. @@ -97,7 +90,7 @@ func putImagesOnSourceBackend(graphicsDriver graphicsdriver.Graphics) { i.usedAsSourceCount++ } if int64(i.usedAsSourceCount) >= int64(baseCountToPutOnSourceBackend*(1< Date: Tue, 30 Apr 2024 00:45:00 +0900 Subject: [PATCH 27/81] .github/workflows: update wasmbrowsertest --- .github/workflows/test.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 97a1489ab..9f26b5bd9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,9 +44,10 @@ jobs: - name: Install wasmbrowsertest run: | - go install github.com/agnivade/wasmbrowsertest@ee76d31b7b9b1645576c1f51fec4c09fe6cf1bb3 + wasmbrowsertest_version=6e494bb3a5ddfe6cccb449250dbdcaa5777b593d + go install github.com/agnivade/wasmbrowsertest@${wasmbrowsertest_version} mv $(go env GOPATH)/bin/wasmbrowsertest${{ runner.os == 'Windows' && '.exe' || '' }} $(go env GOPATH)/bin/go_js_wasm_exec${{ runner.os == 'Windows' && '.exe' || '' }} - go install github.com/agnivade/wasmbrowsertest/cmd/cleanenv@ee76d31b7b9b1645576c1f51fec4c09fe6cf1bb3 + go install github.com/agnivade/wasmbrowsertest/cmd/cleanenv@${wasmbrowsertest_version} - name: Prepare ebitenmobile test run: | From 35e29a29e751984d8ee93132a24a238086abfdb1 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Tue, 30 Apr 2024 23:32:54 +0900 Subject: [PATCH 28/81] internal/ui: bug fix: wrong property names were specified Closes #2975 --- ebiten_test.go | 32 ++++++++++++++++++++++++ internal/ui/screensizeinfullscreen_js.go | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 ebiten_test.go diff --git a/ebiten_test.go b/ebiten_test.go new file mode 100644 index 000000000..bf0ceb254 --- /dev/null +++ b/ebiten_test.go @@ -0,0 +1,32 @@ +// Copyright 2024 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ebiten_test + +import ( + "testing" + + "github.com/hajimehoshi/ebiten/v2" +) + +func TestScreenSizeInFullscreen(t *testing.T) { + // Just call ScreenSizeInFullscreen. There was a crash bug on browsers (#2975). + w, h := ebiten.ScreenSizeInFullscreen() + if w <= 0 { + t.Errorf("w must be positive but not: %d", w) + } + if h <= 0 { + t.Errorf("h must be positive but not: %d", h) + } +} diff --git a/internal/ui/screensizeinfullscreen_js.go b/internal/ui/screensizeinfullscreen_js.go index 40ed9b5c0..336400614 100644 --- a/internal/ui/screensizeinfullscreen_js.go +++ b/internal/ui/screensizeinfullscreen_js.go @@ -16,5 +16,5 @@ package ui func (u *UserInterface) ScreenSizeInFullscreen() (int, int) { // On browsers, ScreenSizeInFullscreen returns the 'window' (global object) size, not 'screen' size for backward compatibility (#2145). - return window.Get("width").Int(), window.Get("height").Int() + return window.Get("innerWidth").Int(), window.Get("innerHeight").Int() } From 903ab6727b957ed44a63832421eeff3e11f28f50 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 3 May 2024 16:35:39 +0900 Subject: [PATCH 29/81] internal/ui: better panic message at ReadPixels before RunGame Closes #2979 --- internal/ui/ui.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/ui/ui.go b/internal/ui/ui.go index 82c2ff546..c04d1c859 100644 --- a/internal/ui/ui.go +++ b/internal/ui/ui.go @@ -126,6 +126,10 @@ func newUserInterface() (*UserInterface, error) { } func (u *UserInterface) readPixels(mipmap *mipmap.Mipmap, pixels []byte, region image.Rectangle) error { + if !u.running.Load() { + panic("ui: ReadPixels cannot be called before the game starts") + } + ok, err := mipmap.ReadPixels(u.graphicsDriver, pixels, region) if err != nil { return err From c658a251710a5d862f049df4c8edbf50ae18700b Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 3 May 2024 23:39:36 +0900 Subject: [PATCH 30/81] all: replace execabs with os/exec os/exec no longer searches executablse in the current directory as of Go 1.19. See https://go.dev/blog/path-security and https://go.dev/issue/43724. --- cmd/ebitenmobile/gobind.go | 2 +- cmd/ebitenmobile/gomobile.go | 2 +- cmd/ebitenmobile/main.go | 2 +- internal/png/gen.go | 2 +- internal/processtest/processtest_test.go | 3 +-- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/cmd/ebitenmobile/gobind.go b/cmd/ebitenmobile/gobind.go index db23da703..18ccb9a6a 100644 --- a/cmd/ebitenmobile/gobind.go +++ b/cmd/ebitenmobile/gobind.go @@ -23,10 +23,10 @@ import ( "fmt" "log" "os" + "os/exec" "path/filepath" "strings" - exec "golang.org/x/sys/execabs" "golang.org/x/tools/go/packages" ) diff --git a/cmd/ebitenmobile/gomobile.go b/cmd/ebitenmobile/gomobile.go index 560155ac3..29ad52d55 100644 --- a/cmd/ebitenmobile/gomobile.go +++ b/cmd/ebitenmobile/gomobile.go @@ -18,13 +18,13 @@ import ( _ "embed" "fmt" "os" + "os/exec" "path/filepath" "runtime" "runtime/debug" // Add a dependency on gomobile in order to get the version via debug.ReadBuildInfo(). _ "github.com/ebitengine/gomobile/geom" - exec "golang.org/x/sys/execabs" ) //go:embed gobind.go diff --git a/cmd/ebitenmobile/main.go b/cmd/ebitenmobile/main.go index da6e60418..dbdef5558 100644 --- a/cmd/ebitenmobile/main.go +++ b/cmd/ebitenmobile/main.go @@ -27,12 +27,12 @@ import ( "fmt" "log" "os" + "os/exec" "path/filepath" "strings" "text/template" "unicode" - exec "golang.org/x/sys/execabs" "golang.org/x/tools/go/packages" ) diff --git a/internal/png/gen.go b/internal/png/gen.go index 4020ede23..b4e4dfce4 100644 --- a/internal/png/gen.go +++ b/internal/png/gen.go @@ -23,13 +23,13 @@ import ( "go/parser" "go/token" "os" + "os/exec" "path/filepath" "regexp" "runtime" "strconv" "strings" - exec "golang.org/x/sys/execabs" "golang.org/x/tools/go/ast/astutil" ) diff --git a/internal/processtest/processtest_test.go b/internal/processtest/processtest_test.go index 1d814b9df..e72e0df3a 100644 --- a/internal/processtest/processtest_test.go +++ b/internal/processtest/processtest_test.go @@ -20,14 +20,13 @@ import ( "bytes" "context" "os" + "os/exec" "path/filepath" "runtime" "strings" "sync" "testing" "time" - - exec "golang.org/x/sys/execabs" ) func isWSL() (bool, error) { From 1ebfa8b911455887f820868b5e966856b56d6df4 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 4 May 2024 00:26:37 +0900 Subject: [PATCH 31/81] internal/ui: refactoring: remove unused code --- internal/ui/context.go | 16 ---------------- internal/ui/image.go | 4 ++-- internal/ui/monitor_glfw.go | 7 ------- internal/ui/ui_darwin.go | 1 - 4 files changed, 2 insertions(+), 26 deletions(-) diff --git a/internal/ui/context.go b/internal/ui/context.go index ff419c975..755f6d650 100644 --- a/internal/ui/context.go +++ b/internal/ui/context.go @@ -73,21 +73,6 @@ func (c *context) updateFrame(graphicsDriver graphicsdriver.Graphics, outsideWid return c.updateFrameImpl(graphicsDriver, clock.UpdateFrame(), outsideWidth, outsideHeight, deviceScaleFactor, ui, false) } -func (c *context) forceUpdateFrame(graphicsDriver graphicsdriver.Graphics, outsideWidth, outsideHeight float64, deviceScaleFactor float64, ui *UserInterface) error { - n := 1 - if ui.GraphicsLibrary() == GraphicsLibraryDirectX { - // On DirectX, both framebuffers in the swap chain should be updated. - // Or, the rendering result becomes unexpected when the window is resized. - n = 2 - } - for i := 0; i < n; i++ { - if err := c.updateFrameImpl(graphicsDriver, 1, outsideWidth, outsideHeight, deviceScaleFactor, ui, true); err != nil { - return err - } - } - return nil -} - func (c *context) updateFrameImpl(graphicsDriver graphicsdriver.Graphics, updateCount int, outsideWidth, outsideHeight float64, deviceScaleFactor float64, ui *UserInterface, forceDraw bool) (err error) { // The given outside size can be 0 e.g. just after restoring from the fullscreen mode on Windows (#1589) // Just ignore such cases. Otherwise, creating a zero-sized framebuffer causes a panic. @@ -308,7 +293,6 @@ func (c *context) runInFrame(f func()) { f() } <-ch - return } func (c *context) processFuncsInFrame(ui *UserInterface) error { diff --git a/internal/ui/image.go b/internal/ui/image.go index 0a971a8c4..093bfb654 100644 --- a/internal/ui/image.go +++ b/internal/ui/image.go @@ -98,7 +98,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [ i.bigOffscreenBuffer = i.ui.newBigOffscreenImage(i, imageType) } - i.bigOffscreenBuffer.drawTriangles(srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule, canSkipMipmap, false) + i.bigOffscreenBuffer.drawTriangles(srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule, canSkipMipmap) return } @@ -217,7 +217,7 @@ func (i *bigOffscreenImage) deallocate() { i.dirty = false } -func (i *bigOffscreenImage) drawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule, canSkipMipmap bool, antialias bool) { +func (i *bigOffscreenImage) drawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule, canSkipMipmap bool) { if i.blend != blend { i.flush() } diff --git a/internal/ui/monitor_glfw.go b/internal/ui/monitor_glfw.go index d491e993b..ef8feb95b 100644 --- a/internal/ui/monitor_glfw.go +++ b/internal/ui/monitor_glfw.go @@ -97,13 +97,6 @@ func (m *monitors) primaryMonitor() *Monitor { return m.monitors[0] } -func (m *monitors) monitorFromID(id int) *Monitor { - m.m.Lock() - defer m.m.Unlock() - - return m.monitors[id] -} - // monitorFromPosition returns a monitor for the given position (x, y), // or returns nil if monitor is not found. // The position is in GLFW pixels. diff --git a/internal/ui/ui_darwin.go b/internal/ui/ui_darwin.go index 7d3a36c09..f1fd13f3f 100644 --- a/internal/ui/ui_darwin.go +++ b/internal/ui/ui_darwin.go @@ -242,7 +242,6 @@ var ( sel_setOrigResizable = objc.RegisterName("setOrigResizable:") sel_toggleFullScreen = objc.RegisterName("toggleFullScreen:") sel_windowDidBecomeKey = objc.RegisterName("windowDidBecomeKey:") - sel_windowDidDeminiaturize = objc.RegisterName("windowDidDeminiaturize:") sel_windowDidEnterFullScreen = objc.RegisterName("windowDidEnterFullScreen:") sel_windowDidExitFullScreen = objc.RegisterName("windowDidExitFullScreen:") sel_windowDidMiniaturize = objc.RegisterName("windowDidMiniaturize:") From c3b9afe8c4c3638c30c3c5bb86968141c646760f Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 4 May 2024 00:30:43 +0900 Subject: [PATCH 32/81] internal/ui: bug fix: compile error for browsers --- internal/ui/context.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/internal/ui/context.go b/internal/ui/context.go index 755f6d650..48274c171 100644 --- a/internal/ui/context.go +++ b/internal/ui/context.go @@ -73,6 +73,21 @@ func (c *context) updateFrame(graphicsDriver graphicsdriver.Graphics, outsideWid return c.updateFrameImpl(graphicsDriver, clock.UpdateFrame(), outsideWidth, outsideHeight, deviceScaleFactor, ui, false) } +func (c *context) forceUpdateFrame(graphicsDriver graphicsdriver.Graphics, outsideWidth, outsideHeight float64, deviceScaleFactor float64, ui *UserInterface) error { + n := 1 + if ui.GraphicsLibrary() == GraphicsLibraryDirectX { + // On DirectX, both framebuffers in the swap chain should be updated. + // Or, the rendering result becomes unexpected when the window is resized. + n = 2 + } + for i := 0; i < n; i++ { + if err := c.updateFrameImpl(graphicsDriver, 1, outsideWidth, outsideHeight, deviceScaleFactor, ui, true); err != nil { + return err + } + } + return nil +} + func (c *context) updateFrameImpl(graphicsDriver graphicsdriver.Graphics, updateCount int, outsideWidth, outsideHeight float64, deviceScaleFactor float64, ui *UserInterface, forceDraw bool) (err error) { // The given outside size can be 0 e.g. just after restoring from the fullscreen mode on Windows (#1589) // Just ignore such cases. Otherwise, creating a zero-sized framebuffer causes a panic. From 322ad99568389991e8d28a5af7aa8e2a603357e1 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 4 May 2024 17:30:15 +0900 Subject: [PATCH 33/81] audio/mp3, audio/vorbis, audio/wav: add comments about cache --- audio/mp3/decode.go | 3 +++ audio/vorbis/vorbis.go | 3 +++ audio/wav/decode.go | 3 +++ 3 files changed, 9 insertions(+) diff --git a/audio/mp3/decode.go b/audio/mp3/decode.go index 4d43d8b63..05c7a177c 100644 --- a/audio/mp3/decode.go +++ b/audio/mp3/decode.go @@ -87,6 +87,9 @@ func DecodeWithoutResampling(src io.Reader) (*Stream, error) { // // A Stream doesn't close src even if src implements io.Closer. // Closing the source is src owner's responsibility. +// +// Resampling can be a very heavy task. Stream has a cache for resampling, but the size is limited. +// Do not expect that Stream has a resampling cache even after whole data is played. func DecodeWithSampleRate(sampleRate int, src io.Reader) (*Stream, error) { d, err := mp3.NewDecoder(src) if err != nil { diff --git a/audio/vorbis/vorbis.go b/audio/vorbis/vorbis.go index 63cf13ee1..71381ab9d 100644 --- a/audio/vorbis/vorbis.go +++ b/audio/vorbis/vorbis.go @@ -182,6 +182,9 @@ func DecodeWithoutResampling(src io.Reader) (*Stream, error) { // // A Stream doesn't close src even if src implements io.Closer. // Closing the source is src owner's responsibility. +// +// Resampling can be a very heavy task. Stream has a cache for resampling, but the size is limited. +// Do not expect that Stream has a resampling cache even after whole data is played. func DecodeWithSampleRate(sampleRate int, src io.Reader) (*Stream, error) { decoded, channelCount, origSampleRate, err := decode(src) if err != nil { diff --git a/audio/wav/decode.go b/audio/wav/decode.go index 1dc0489b3..abd8593b7 100644 --- a/audio/wav/decode.go +++ b/audio/wav/decode.go @@ -134,6 +134,9 @@ func DecodeWithoutResampling(src io.Reader) (*Stream, error) { // // A Stream doesn't close src even if src implements io.Closer. // Closing the source is src owner's responsibility. +// +// Resampling can be a very heavy task. Stream has a cache for resampling, but the size is limited. +// Do not expect that Stream has a resampling cache even after whole data is played. func DecodeWithSampleRate(sampleRate int, src io.Reader) (*Stream, error) { s, origSampleRate, err := decode(src) if err != nil { From ca9a80600d8320ef26c2230f8ea1fb598790f25e Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 4 May 2024 19:09:02 +0900 Subject: [PATCH 34/81] internal/graphicsdriver/metal: release MTLLibrary --- .../graphicsdriver/metal/mtl/mtl_darwin.go | 4 + .../graphicsdriver/metal/shader_darwin.go | 8 +- internal/processtest/testdata/shader.go | 74 +++++++++++++++++++ 3 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 internal/processtest/testdata/shader.go diff --git a/internal/graphicsdriver/metal/mtl/mtl_darwin.go b/internal/graphicsdriver/metal/mtl/mtl_darwin.go index a79e60455..118202696 100644 --- a/internal/graphicsdriver/metal/mtl/mtl_darwin.go +++ b/internal/graphicsdriver/metal/mtl/mtl_darwin.go @@ -1028,6 +1028,10 @@ func (l Library) MakeFunction(name string) (Function, error) { return Function{f}, nil } +func (l Library) Release() { + l.library.Send(sel_release) +} + // Texture is a memory allocation for storing formatted // image data that is accessible to the GPU. // diff --git a/internal/graphicsdriver/metal/shader_darwin.go b/internal/graphicsdriver/metal/shader_darwin.go index 428fab19f..253143f8e 100644 --- a/internal/graphicsdriver/metal/shader_darwin.go +++ b/internal/graphicsdriver/metal/shader_darwin.go @@ -33,6 +33,7 @@ type Shader struct { id graphicsdriver.ShaderID ir *shaderir.Program + lib mtl.Library fs mtl.Function vs mtl.Function rpss map[shaderRpsKey]mtl.RenderPipelineState @@ -60,6 +61,7 @@ func (s *Shader) Dispose() { } s.vs.Release() s.fs.Release() + s.lib.Release() } func (s *Shader) init(device mtl.Device) error { @@ -68,11 +70,13 @@ func (s *Shader) init(device mtl.Device) error { if err != nil { return fmt.Errorf("metal: device.MakeLibrary failed: %w, source: %s", err, src) } - vs, err := lib.MakeFunction(msl.VertexName) + s.lib = lib + + vs, err := s.lib.MakeFunction(msl.VertexName) if err != nil { return fmt.Errorf("metal: lib.MakeFunction for vertex failed: %w, source: %s", err, src) } - fs, err := lib.MakeFunction(msl.FragmentName) + fs, err := s.lib.MakeFunction(msl.FragmentName) if err != nil { return fmt.Errorf("metal: lib.MakeFunction for fragment failed: %w, source: %s", err, src) } diff --git a/internal/processtest/testdata/shader.go b/internal/processtest/testdata/shader.go new file mode 100644 index 000000000..cae4d3bb6 --- /dev/null +++ b/internal/processtest/testdata/shader.go @@ -0,0 +1,74 @@ +// Copyright 2024 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build ignore + +package main + +import ( + "fmt" + + "github.com/hajimehoshi/ebiten/v2" +) + +// This test confirms that deallocation of a shader works correctly. + +type Game struct { + count int + + img *ebiten.Image +} + +func (g *Game) Update() error { + if g.img == nil { + g.img = ebiten.NewImage(1, 1) + } + + g.count++ + + s, err := ebiten.NewShader([]byte(fmt.Sprintf(`//kage:unit pixels + +package main + +func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 { + return vec4(%d/255.0) +} +`, g.count))) + if err != nil { + return err + } + + // Use the shader to ensure that the shader is actually allocated. + g.img.DrawRectShader(1, 1, s, nil) + + s.Deallocate() + + if g.count == 60 { + return ebiten.Termination + } + return nil +} + +func (g *Game) Draw(screen *ebiten.Image) { +} + +func (g *Game) Layout(w, h int) (int, int) { + return 320, 240 +} + +func main() { + if err := ebiten.RunGame(&Game{}); err != nil { + panic(err) + } +} From d7df5ebcbdf8fcdf7df7949e3eb49ff66a5cf52f Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 5 May 2024 02:18:14 +0900 Subject: [PATCH 35/81] internal/builtinshader: move a clearing shader to builtinshader --- internal/atlas/shader.go | 8 +------- internal/builtinshader/shader.go | 9 +++++++++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/internal/atlas/shader.go b/internal/atlas/shader.go index f90a5b0b7..32d2312f0 100644 --- a/internal/atlas/shader.go +++ b/internal/atlas/shader.go @@ -104,13 +104,7 @@ func init() { return nil }) wg.Go(func() error { - ir, err := graphics.CompileShader([]byte(`//kage:unit pixels - -package main - -func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 { - return vec4(0) -}`)) + ir, err := graphics.CompileShader([]byte(builtinshader.ClearShaderSource)) if err != nil { return fmt.Errorf("atlas: compiling the clear shader failed: %w", err) } diff --git a/internal/builtinshader/shader.go b/internal/builtinshader/shader.go index e5a60abfe..1ff0ccd37 100644 --- a/internal/builtinshader/shader.go +++ b/internal/builtinshader/shader.go @@ -188,3 +188,12 @@ func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 { return mix(mix(c0, c1, rate.x), mix(c2, c3, rate.x), rate.y) } `) + +var ClearShaderSource = []byte(`//kage:unit pixels + +package main + +func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 { + return vec4(0) +} +`) From c46f62e1846517b4d4ee241f067987b411234c12 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 20 Apr 2024 23:48:33 +0900 Subject: [PATCH 36/81] all: add a new package shaderprecomp The current implementation is only for macOS so far. Updates #2861 --- .gitignore | 3 + examples/shaderprecomp/defaultshader.go | 33 ++++++ examples/shaderprecomp/main.go | 73 +++++++++++++ .../shaderprecomp/metallib/dummy.metallib | 1 + examples/shaderprecomp/metallib/gen.go | 103 ++++++++++++++++++ examples/shaderprecomp/metallib/generate.go | 17 +++ examples/shaderprecomp/register_darwin.go | 52 +++++++++ examples/shaderprecomp/register_others.go | 27 +++++ internal/builtinshader/shader.go | 10 ++ internal/graphics/shader.go | 25 ++++- .../metal/mtl/dispatch_darwin.go | 39 +++++++ .../graphicsdriver/metal/mtl/mtl_darwin.go | 22 ++++ .../graphicsdriver/metal/shader_darwin.go | 69 ++++++++++-- internal/shader/shader.go | 1 + internal/shaderir/program.go | 19 ++++ shaderprecomp/shaderprecomp.go | 70 ++++++++++++ shaderprecomp/shaderprecomp_darwin.go | 47 ++++++++ 17 files changed, 599 insertions(+), 12 deletions(-) create mode 100644 examples/shaderprecomp/defaultshader.go create mode 100644 examples/shaderprecomp/main.go create mode 100644 examples/shaderprecomp/metallib/dummy.metallib create mode 100644 examples/shaderprecomp/metallib/gen.go create mode 100644 examples/shaderprecomp/metallib/generate.go create mode 100644 examples/shaderprecomp/register_darwin.go create mode 100644 examples/shaderprecomp/register_others.go create mode 100644 internal/graphicsdriver/metal/mtl/dispatch_darwin.go create mode 100644 shaderprecomp/shaderprecomp.go create mode 100644 shaderprecomp/shaderprecomp_darwin.go diff --git a/.gitignore b/.gitignore index 14ecd3c00..719c6998d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,6 @@ .vscode go.work go.work.sum + +*.metallib +!dummy.metallib diff --git a/examples/shaderprecomp/defaultshader.go b/examples/shaderprecomp/defaultshader.go new file mode 100644 index 000000000..1c7f70197 --- /dev/null +++ b/examples/shaderprecomp/defaultshader.go @@ -0,0 +1,33 @@ +// Copyright 2020 The Ebiten Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build ignore + +//kage:unit pixels + +package main + +var Time float +var Cursor vec2 + +func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 { + pos := (dstPos.xy - imageDstOrigin()) / imageDstSize() + pos += Cursor / imageDstSize() / 4 + clr := 0.0 + clr += sin(pos.x*cos(Time/15)*80) + cos(pos.y*cos(Time/15)*10) + clr += sin(pos.y*sin(Time/10)*40) + cos(pos.x*sin(Time/25)*40) + clr += sin(pos.x*sin(Time/5)*10) + sin(pos.y*sin(Time/35)*80) + clr *= sin(Time/10) * 0.5 + return vec4(clr, clr*0.5, sin(clr+Time/3)*0.75, 1) +} diff --git a/examples/shaderprecomp/main.go b/examples/shaderprecomp/main.go new file mode 100644 index 000000000..971d8aa04 --- /dev/null +++ b/examples/shaderprecomp/main.go @@ -0,0 +1,73 @@ +// Copyright 2024 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + _ "embed" + "log" + + "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/ebitenutil" +) + +//go:embed defaultshader.go +var defaultShaderSourceBytes []byte + +type Game struct { + defaultShader *ebiten.Shader + counter int +} + +func (g *Game) Update() error { + g.counter++ + + if g.defaultShader == nil { + s, err := ebiten.NewShader(defaultShaderSourceBytes) + if err != nil { + return err + } + g.defaultShader = s + } + return nil +} + +func (g *Game) Draw(screen *ebiten.Image) { + cx, cy := ebiten.CursorPosition() + w, h := screen.Bounds().Dx(), screen.Bounds().Dy() + op := &ebiten.DrawRectShaderOptions{} + op.Uniforms = map[string]interface{}{ + "Time": float32(g.counter) / float32(ebiten.TPS()), + "Cursor": []float32{float32(cx), float32(cy)}, + } + screen.DrawRectShader(w, h, g.defaultShader, op) + + msg := `This is a test for shader precompilation. +Precompilation works only on macOS so far. +Note that this example still works even without shader precompilation.` + ebitenutil.DebugPrint(screen, msg) +} + +func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) { + return outsideWidth, outsideHeight +} + +func main() { + if err := registerPrecompiledShaders(); err != nil { + log.Fatal(err) + } + if err := ebiten.RunGame(&Game{}); err != nil { + log.Fatal(err) + } +} diff --git a/examples/shaderprecomp/metallib/dummy.metallib b/examples/shaderprecomp/metallib/dummy.metallib new file mode 100644 index 000000000..4f8a5ca8c --- /dev/null +++ b/examples/shaderprecomp/metallib/dummy.metallib @@ -0,0 +1 @@ +This is a dummy .metallib file to trick Go's embed package. diff --git a/examples/shaderprecomp/metallib/gen.go b/examples/shaderprecomp/metallib/gen.go new file mode 100644 index 000000000..b15413185 --- /dev/null +++ b/examples/shaderprecomp/metallib/gen.go @@ -0,0 +1,103 @@ +// Copyright 2024 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build ignore + +// This is a program to generate precompiled Metal libraries. +// +// See https://developer.apple.com/documentation/metal/shader_libraries/building_a_shader_library_by_precompiling_source_files. +package main + +import ( + "os" + "os/exec" + "path/filepath" + + "golang.org/x/sync/errgroup" + + "github.com/hajimehoshi/ebiten/v2/shaderprecomp" +) + +func main() { + if err := run(); err != nil { + panic(err) + } +} + +func run() error { + tmpdir, err := os.MkdirTemp("", "") + if err != nil { + return err + } + defer os.RemoveAll(tmpdir) + + srcs := shaderprecomp.AppendBuildinShaderSources(nil) + + defaultSrcBytes, err := os.ReadFile(filepath.Join("..", "defaultshader.go")) + if err != nil { + return err + } + defaultSrc, err := shaderprecomp.NewShaderSource(defaultSrcBytes) + if err != nil { + return err + } + srcs = append(srcs, defaultSrc) + + var wg errgroup.Group + for _, src := range srcs { + source := src + wg.Go(func() error { + return compile(source, tmpdir) + }) + } + if err := wg.Wait(); err != nil { + return err + } + return nil +} + +func compile(source *shaderprecomp.ShaderSource, tmpdir string) error { + id := source.ID().String() + + metalFilePath := filepath.Join(tmpdir, id+".metal") + + f, err := os.Create(metalFilePath) + if err != nil { + return err + } + defer f.Close() + + if err := shaderprecomp.CompileToMSL(f, source); err != nil { + return err + } + if err := f.Sync(); err != nil { + return err + } + + irFilePath := filepath.Join(tmpdir, id+".ir") + cmd := exec.Command("xcrun", "-sdk", "macosx", "metal", "-o", irFilePath, "-c", metalFilePath) + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return err + } + + metallibFilePath := id + ".metallib" + cmd = exec.Command("xcrun", "-sdk", "macosx", "metallib", "-o", metallibFilePath, irFilePath) + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return err + } + + return nil +} diff --git a/examples/shaderprecomp/metallib/generate.go b/examples/shaderprecomp/metallib/generate.go new file mode 100644 index 000000000..efd32a56d --- /dev/null +++ b/examples/shaderprecomp/metallib/generate.go @@ -0,0 +1,17 @@ +// Copyright 2024 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:generate go run gen.go + +package metallib diff --git a/examples/shaderprecomp/register_darwin.go b/examples/shaderprecomp/register_darwin.go new file mode 100644 index 000000000..c33568138 --- /dev/null +++ b/examples/shaderprecomp/register_darwin.go @@ -0,0 +1,52 @@ +// Copyright 2024 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "embed" + "errors" + "fmt" + "io/fs" + "os" + + "github.com/hajimehoshi/ebiten/v2/shaderprecomp" +) + +//go:embed metallib/*.metallib +var metallibs embed.FS + +func registerPrecompiledShaders() error { + srcs := shaderprecomp.AppendBuildinShaderSources(nil) + defaultShaderSource, err := shaderprecomp.NewShaderSource(defaultShaderSourceBytes) + if err != nil { + return err + } + srcs = append(srcs, defaultShaderSource) + + for _, src := range srcs { + name := src.ID().String() + ".metallib" + lib, err := metallibs.ReadFile("metallib/" + name) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + fmt.Fprintf(os.Stderr, "precompiled Metal library %s was not found. Run 'go generate' for 'metallib' directory to generate them\n", name) + continue + } + return err + } + shaderprecomp.RegisterMetalLibrary(src, lib) + } + + return nil +} diff --git a/examples/shaderprecomp/register_others.go b/examples/shaderprecomp/register_others.go new file mode 100644 index 000000000..2f9c757ef --- /dev/null +++ b/examples/shaderprecomp/register_others.go @@ -0,0 +1,27 @@ +// Copyright 2024 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !darwin + +package main + +import ( + "fmt" + "os" +) + +func registerPrecompiledShaders() error { + fmt.Fprintf(os.Stderr, "precompiled shaders are not available in this environment.\n") + return nil +} diff --git a/internal/builtinshader/shader.go b/internal/builtinshader/shader.go index 1ff0ccd37..3e168f2e5 100644 --- a/internal/builtinshader/shader.go +++ b/internal/builtinshader/shader.go @@ -197,3 +197,13 @@ func Fragment(dstPos vec4, srcPos vec2, color vec4) vec4 { return vec4(0) } `) + +func AppendShaderSources(sources [][]byte) [][]byte { + for filter := Filter(0); filter < FilterCount; filter++ { + for address := Address(0); address < AddressCount; address++ { + sources = append(sources, ShaderSource(filter, address, false), ShaderSource(filter, address, true)) + } + } + sources = append(sources, ScreenShaderSource, ClearShaderSource) + return sources +} diff --git a/internal/graphics/shader.go b/internal/graphics/shader.go index e4e95c701..059f53583 100644 --- a/internal/graphics/shader.go +++ b/internal/graphics/shader.go @@ -161,8 +161,8 @@ func __vertex(dstPos vec2, srcPos vec2, color vec4) (vec4, vec2, vec4) { return shaderSuffix, nil } -func CompileShader(src []byte) (*shaderir.Program, error) { - unit, err := shader.ParseCompilerDirectives(src) +func completeShaderSource(fragmentSrc []byte) ([]byte, error) { + unit, err := shader.ParseCompilerDirectives(fragmentSrc) if err != nil { return nil, err } @@ -172,14 +172,23 @@ func CompileShader(src []byte) (*shaderir.Program, error) { } var buf bytes.Buffer - buf.Write(src) + buf.Write(fragmentSrc) buf.WriteString(suffix) + return buf.Bytes(), nil +} + +func CompileShader(fragmentSrc []byte) (*shaderir.Program, error) { + src, err := completeShaderSource(fragmentSrc) + if err != nil { + return nil, err + } + const ( vert = "__vertex" frag = "Fragment" ) - ir, err := shader.Compile(buf.Bytes(), vert, frag, ShaderImageCount) + ir, err := shader.Compile(src, vert, frag, ShaderImageCount) if err != nil { return nil, err } @@ -193,3 +202,11 @@ func CompileShader(src []byte) (*shaderir.Program, error) { return ir, nil } + +func CalcSourceHash(fragmentSrc []byte) (shaderir.SourceHash, error) { + src, err := completeShaderSource(fragmentSrc) + if err != nil { + return shaderir.SourceHash{}, err + } + return shaderir.CalcSourceHash(src), nil +} diff --git a/internal/graphicsdriver/metal/mtl/dispatch_darwin.go b/internal/graphicsdriver/metal/mtl/dispatch_darwin.go new file mode 100644 index 000000000..f9dc1bacb --- /dev/null +++ b/internal/graphicsdriver/metal/mtl/dispatch_darwin.go @@ -0,0 +1,39 @@ +// Copyright 2024 The Ebiten Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mtl + +import ( + "unsafe" + + "github.com/ebitengine/purego" +) + +var libSystem uintptr + +var ( + dispatchDataCreate func(buffer unsafe.Pointer, size uint, queue uintptr, destructor uintptr) uintptr + dispatchRelease func(obj uintptr) +) + +func init() { + lib, err := purego.Dlopen("/usr/lib/libSystem.B.dylib", purego.RTLD_LAZY|purego.RTLD_GLOBAL) + if err != nil { + panic(err) + } + libSystem = lib + + purego.RegisterLibFunc(&dispatchDataCreate, libSystem, "dispatch_data_create") + purego.RegisterLibFunc(&dispatchRelease, libSystem, "dispatch_release") +} diff --git a/internal/graphicsdriver/metal/mtl/mtl_darwin.go b/internal/graphicsdriver/metal/mtl/mtl_darwin.go index 118202696..4d2e065d2 100644 --- a/internal/graphicsdriver/metal/mtl/mtl_darwin.go +++ b/internal/graphicsdriver/metal/mtl/mtl_darwin.go @@ -493,6 +493,7 @@ var ( sel_supportsFeatureSet = objc.RegisterName("supportsFeatureSet:") sel_newCommandQueue = objc.RegisterName("newCommandQueue") sel_newLibraryWithSource_options_error = objc.RegisterName("newLibraryWithSource:options:error:") + sel_newLibraryWithData_error = objc.RegisterName("newLibraryWithData:error:") sel_release = objc.RegisterName("release") sel_retain = objc.RegisterName("retain") sel_new = objc.RegisterName("new") @@ -652,6 +653,27 @@ func (d Device) MakeLibrary(source string, opt CompileOptions) (Library, error) return Library{l}, nil } +// MakeLibraryWithData creates a Metal library instance from a data instance that contains the functions in a precompiled Metal library. +// +// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433391-makelibrary +func (d Device) MakeLibraryWithData(buffer []byte) (Library, error) { + defer runtime.KeepAlive(buffer) + + data := dispatchDataCreate(unsafe.Pointer(&buffer[0]), uint(len(buffer)), 0, 0) + defer dispatchRelease(data) + + var err cocoa.NSError + l := d.device.Send( + sel_newLibraryWithData_error, + data, + unsafe.Pointer(&err), + ) + if l == 0 { + return Library{}, errors.New(cocoa.NSString{ID: err.Send(sel_localizedDescription)}.String()) + } + return Library{l}, nil +} + // MakeRenderPipelineState creates a render pipeline state object. // // Reference: https://developer.apple.com/documentation/metal/mtldevice/1433369-makerenderpipelinestate. diff --git a/internal/graphicsdriver/metal/shader_darwin.go b/internal/graphicsdriver/metal/shader_darwin.go index 253143f8e..fcb46ac88 100644 --- a/internal/graphicsdriver/metal/shader_darwin.go +++ b/internal/graphicsdriver/metal/shader_darwin.go @@ -16,6 +16,7 @@ package metal import ( "fmt" + "sync" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/mtl" @@ -23,6 +24,38 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/shaderir/msl" ) +type precompiledLibraries struct { + binaries map[shaderir.SourceHash][]byte + m sync.Mutex +} + +func (c *precompiledLibraries) put(hash shaderir.SourceHash, bin []byte) { + c.m.Lock() + defer c.m.Unlock() + + if c.binaries == nil { + c.binaries = map[shaderir.SourceHash][]byte{} + } + if _, ok := c.binaries[hash]; ok { + panic(fmt.Sprintf("metal: the precompiled library for the hash %s is already registered", hash.String())) + } + c.binaries[hash] = bin +} + +func (c *precompiledLibraries) get(hash shaderir.SourceHash) ([]byte, bool) { + c.m.Lock() + defer c.m.Unlock() + + bin, ok := c.binaries[hash] + return bin, ok +} + +var thePrecompiledLibraries precompiledLibraries + +func RegisterPrecompiledLibrary(hash shaderir.SourceHash, bin []byte) { + thePrecompiledLibraries.put(hash, bin) +} + type shaderRpsKey struct { blend graphicsdriver.Blend stencilMode stencilMode @@ -37,6 +70,8 @@ type Shader struct { fs mtl.Function vs mtl.Function rpss map[shaderRpsKey]mtl.RenderPipelineState + + libraryPrecompiled bool } func newShader(device mtl.Device, id graphicsdriver.ShaderID, program *shaderir.Program) (*Shader, error) { @@ -61,24 +96,42 @@ func (s *Shader) Dispose() { } s.vs.Release() s.fs.Release() - s.lib.Release() + // Do not release s.lib if this is precompiled. This is a shared precompiled library. + if !s.libraryPrecompiled { + s.lib.Release() + } } func (s *Shader) init(device mtl.Device) error { - src := msl.Compile(s.ir) - lib, err := device.MakeLibrary(src, mtl.CompileOptions{}) - if err != nil { - return fmt.Errorf("metal: device.MakeLibrary failed: %w, source: %s", err, src) + var src string + if libBin, ok := thePrecompiledLibraries.get(s.ir.SourceHash); ok { + lib, err := device.MakeLibraryWithData(libBin) + if err != nil { + return err + } + s.lib = lib + } else { + src = msl.Compile(s.ir) + lib, err := device.MakeLibrary(src, mtl.CompileOptions{}) + if err != nil { + return fmt.Errorf("metal: device.MakeLibrary failed: %w, source: %s", err, src) + } + s.lib = lib } - s.lib = lib vs, err := s.lib.MakeFunction(msl.VertexName) if err != nil { - return fmt.Errorf("metal: lib.MakeFunction for vertex failed: %w, source: %s", err, src) + if src != "" { + return fmt.Errorf("metal: lib.MakeFunction for vertex failed: %w, source: %s", err, src) + } + return fmt.Errorf("metal: lib.MakeFunction for vertex failed: %w", err) } fs, err := s.lib.MakeFunction(msl.FragmentName) if err != nil { - return fmt.Errorf("metal: lib.MakeFunction for fragment failed: %w, source: %s", err, src) + if src != "" { + return fmt.Errorf("metal: lib.MakeFunction for fragment failed: %w, source: %s", err, src) + } + return fmt.Errorf("metal: lib.MakeFunction for fragment failed: %w", err) } s.fs = fs s.vs = vs diff --git a/internal/shader/shader.go b/internal/shader/shader.go index 12626b12d..407e815d5 100644 --- a/internal/shader/shader.go +++ b/internal/shader/shader.go @@ -202,6 +202,7 @@ func Compile(src []byte, vertexEntry, fragmentEntry string, textureCount int) (* fragmentEntry: fragmentEntry, unit: unit, } + s.ir.SourceHash = shaderir.CalcSourceHash(src) s.global.ir = &shaderir.Block{} s.parse(f) diff --git a/internal/shaderir/program.go b/internal/shaderir/program.go index 792358ab9..8688057cb 100644 --- a/internal/shaderir/program.go +++ b/internal/shaderir/program.go @@ -16,8 +16,10 @@ package shaderir import ( + "encoding/hex" "go/constant" "go/token" + "hash/fnv" "sort" "strings" ) @@ -29,6 +31,21 @@ const ( Pixels ) +type SourceHash [16]byte + +func CalcSourceHash(source []byte) SourceHash { + h := fnv.New128a() + _, _ = h.Write(source) + + var hash SourceHash + h.Sum(hash[:0]) + return hash +} + +func (s SourceHash) String() string { + return hex.EncodeToString(s[:]) +} + type Program struct { UniformNames []string Uniforms []Type @@ -40,6 +57,8 @@ type Program struct { FragmentFunc FragmentFunc Unit Unit + SourceHash SourceHash + uniformFactors []uint32 } diff --git a/shaderprecomp/shaderprecomp.go b/shaderprecomp/shaderprecomp.go new file mode 100644 index 000000000..650ebc1b5 --- /dev/null +++ b/shaderprecomp/shaderprecomp.go @@ -0,0 +1,70 @@ +// Copyright 2024 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shaderprecomp + +import ( + "github.com/hajimehoshi/ebiten/v2/internal/builtinshader" + "github.com/hajimehoshi/ebiten/v2/internal/graphics" + "github.com/hajimehoshi/ebiten/v2/internal/shaderir" +) + +// AppendBuildinShaderSources appends all the built-in shader sources to the given slice. +// +// Do not modify the content of the shader source. +// +// AppendBuildinShaderSources is concurrent-safe. +func AppendBuildinShaderSources(sources []*ShaderSource) []*ShaderSource { + for _, s := range builtinshader.AppendShaderSources(nil) { + src, err := NewShaderSource(s) + if err != nil { + panic(err) + } + sources = append(sources, src) + } + return sources +} + +// ShaderSource is an object encapsulating a shader source code. +type ShaderSource struct { + source []byte + id ShaderSourceID +} + +// NewShaderSource creates a new ShaderSource object from the given source code. +func NewShaderSource(source []byte) (*ShaderSource, error) { + hash, err := graphics.CalcSourceHash(source) + if err != nil { + return nil, err + } + return &ShaderSource{ + source: source, + id: ShaderSourceID(hash), + }, nil +} + +// ID returns a unique identifier for the shader source. +// The ShaderSourceID value must be the same for the same shader source and the same Ebitengine version. +// There is no guarantee that the ShaderSourceID value is the same between different Ebitengine versions. +func (s *ShaderSource) ID() ShaderSourceID { + return s.id +} + +// ShaderSourceID is a uniuqe identifier for a shader source. +type ShaderSourceID [16]byte + +// String returns a string representation of the shader source ID. +func (s ShaderSourceID) String() string { + return shaderir.SourceHash(s).String() +} diff --git a/shaderprecomp/shaderprecomp_darwin.go b/shaderprecomp/shaderprecomp_darwin.go new file mode 100644 index 000000000..ed2eaa9ce --- /dev/null +++ b/shaderprecomp/shaderprecomp_darwin.go @@ -0,0 +1,47 @@ +// Copyright 2024 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shaderprecomp + +import ( + "io" + + "github.com/hajimehoshi/ebiten/v2/internal/graphics" + "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal" + "github.com/hajimehoshi/ebiten/v2/internal/shaderir" + "github.com/hajimehoshi/ebiten/v2/internal/shaderir/msl" +) + +// CompileToMSL compiles the shader source to Metal Shader Language, and writes the result to w. +// +// CompileToMSL is concurrent-safe. +func CompileToMSL(w io.Writer, source *ShaderSource) error { + ir, err := graphics.CompileShader(source.source) + if err != nil { + return err + } + if _, err = w.Write([]byte(msl.Compile(ir))); err != nil { + return err + } + return nil +} + +// RegisterMetalLibrary registers a precompiled Metal library for a shader source. +// library must be the content of a .metallib file. +// For more details, see https://developer.apple.com/documentation/metal/shader_libraries/building_a_shader_library_by_precompiling_source_files. +// +// RegisterMetalLibrary is concurrent-safe. +func RegisterMetalLibrary(source *ShaderSource, library []byte) { + metal.RegisterPrecompiledLibrary(shaderir.SourceHash(source.ID()), library) +} From 42eef43136fe6ce9f4cd495f1be3a84613ad5247 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 5 May 2024 04:53:14 +0900 Subject: [PATCH 37/81] examples/shaderprecomp: add . --- examples/shaderprecomp/register_darwin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shaderprecomp/register_darwin.go b/examples/shaderprecomp/register_darwin.go index c33568138..b5b250cfd 100644 --- a/examples/shaderprecomp/register_darwin.go +++ b/examples/shaderprecomp/register_darwin.go @@ -40,7 +40,7 @@ func registerPrecompiledShaders() error { lib, err := metallibs.ReadFile("metallib/" + name) if err != nil { if errors.Is(err, fs.ErrNotExist) { - fmt.Fprintf(os.Stderr, "precompiled Metal library %s was not found. Run 'go generate' for 'metallib' directory to generate them\n", name) + fmt.Fprintf(os.Stderr, "precompiled Metal library %s was not found. Run 'go generate' for 'metallib' directory to generate them.\n", name) continue } return err From aace620b7edfb9a026a744e108b3f96503feb7d2 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 5 May 2024 15:10:05 +0900 Subject: [PATCH 38/81] all: update PureGo to v0.8.0-alpha.2 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 934698a3a..0695b54aa 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/ebitengine/gomobile v0.0.0-20240429094902-cf88669c3591 github.com/ebitengine/hideconsole v1.0.0 github.com/ebitengine/oto/v3 v3.3.0-alpha.1 - github.com/ebitengine/purego v0.8.0-alpha.1.0.20240429114929-1ff8716e2490 + github.com/ebitengine/purego v0.8.0-alpha.2 github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f github.com/go-text/typesetting v0.1.1-0.20240402181327-ced1d6822703 github.com/hajimehoshi/bitmapfont/v3 v3.1.0 diff --git a/go.sum b/go.sum index 343b20257..adc16194f 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= github.com/ebitengine/oto/v3 v3.3.0-alpha.1 h1:J2nBmQwPLKc4+yLObytq1jKNydI96l6EjZfgefiqGbk= github.com/ebitengine/oto/v3 v3.3.0-alpha.1/go.mod h1:T2/VV0UWG97GEEf4kORMU2nCneYT/YmwSTxPutSVaUg= -github.com/ebitengine/purego v0.8.0-alpha.1.0.20240429114929-1ff8716e2490 h1:hOHVTszt219H/6Ma0o/RvZ++BPJtj2PMie+3fyA+sTs= -github.com/ebitengine/purego v0.8.0-alpha.1.0.20240429114929-1ff8716e2490/go.mod h1:y8L+ZRLphbdPW2xs41fur/KaW57yTzrFsqsclHyHrTM= +github.com/ebitengine/purego v0.8.0-alpha.2 h1:+Kyr9n4eXAGMzhtWJxfdQ7AzGn0+6ZWihfCCxul3Dso= +github.com/ebitengine/purego v0.8.0-alpha.2/go.mod h1:w5fARo4H5UrAgQTz0yqDfZ6bjstTQwUFmO+TN+nHlWE= github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f h1:ysqRe+lvUiL0dH5XzkH0Bz68bFMPJ4f5Si4L/HD9SGk= github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f/go.mod h1:i/ebyRRv/IoHixuZ9bElZnXbmfoUVPGQpdsJ4sVuX38= github.com/go-text/typesetting v0.1.1-0.20240402181327-ced1d6822703 h1:AqtMl9yw7r319Ah4W2afQm3Ql+PEsQKHds18tGvKhog= From caab1ee29fe9acef8ed2c980c10a206321c20629 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 5 May 2024 17:18:06 +0900 Subject: [PATCH 39/81] internal/graphicsdriver/metal: refactoring --- internal/graphicsdriver/metal/shader_darwin.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/internal/graphicsdriver/metal/shader_darwin.go b/internal/graphicsdriver/metal/shader_darwin.go index fcb46ac88..87823978b 100644 --- a/internal/graphicsdriver/metal/shader_darwin.go +++ b/internal/graphicsdriver/metal/shader_darwin.go @@ -42,12 +42,11 @@ func (c *precompiledLibraries) put(hash shaderir.SourceHash, bin []byte) { c.binaries[hash] = bin } -func (c *precompiledLibraries) get(hash shaderir.SourceHash) ([]byte, bool) { +func (c *precompiledLibraries) get(hash shaderir.SourceHash) []byte { c.m.Lock() defer c.m.Unlock() - bin, ok := c.binaries[hash] - return bin, ok + return c.binaries[hash] } var thePrecompiledLibraries precompiledLibraries @@ -104,7 +103,7 @@ func (s *Shader) Dispose() { func (s *Shader) init(device mtl.Device) error { var src string - if libBin, ok := thePrecompiledLibraries.get(s.ir.SourceHash); ok { + if libBin := thePrecompiledLibraries.get(s.ir.SourceHash); len(libBin) > 0 { lib, err := device.MakeLibraryWithData(libBin) if err != nil { return err From a41af4528bd95d202098a8cb5e44f9717b16ecfc Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 5 May 2024 18:58:13 +0900 Subject: [PATCH 40/81] internal/graphicsdriver/directx: refactoring --- internal/graphicsdriver/directx/graphics11_windows.go | 4 +--- internal/graphicsdriver/directx/graphics12_windows.go | 4 +--- internal/graphicsdriver/directx/shader_windows.go | 8 +++++--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/internal/graphicsdriver/directx/graphics11_windows.go b/internal/graphicsdriver/directx/graphics11_windows.go index 807009046..53035b2f6 100644 --- a/internal/graphicsdriver/directx/graphics11_windows.go +++ b/internal/graphicsdriver/directx/graphics11_windows.go @@ -24,7 +24,6 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/shaderir" - "github.com/hajimehoshi/ebiten/v2/internal/shaderir/hlsl" ) var inputElementDescsForDX11 = []_D3D11_INPUT_ELEMENT_DESC{ @@ -482,8 +481,7 @@ func (g *graphics11) MaxImageSize() int { } func (g *graphics11) NewShader(program *shaderir.Program) (graphicsdriver.Shader, error) { - vs, ps, offsets := hlsl.Compile(program) - vsh, psh, err := compileShader(vs, ps) + vsh, psh, offsets, err := compileShader(program) if err != nil { return nil, err } diff --git a/internal/graphicsdriver/directx/graphics12_windows.go b/internal/graphicsdriver/directx/graphics12_windows.go index 69740b90e..dae2f9c7f 100644 --- a/internal/graphicsdriver/directx/graphics12_windows.go +++ b/internal/graphicsdriver/directx/graphics12_windows.go @@ -25,7 +25,6 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/microsoftgdk" "github.com/hajimehoshi/ebiten/v2/internal/shaderir" - "github.com/hajimehoshi/ebiten/v2/internal/shaderir/hlsl" ) type resourceWithSize struct { @@ -1064,8 +1063,7 @@ func (g *graphics12) MaxImageSize() int { } func (g *graphics12) NewShader(program *shaderir.Program) (graphicsdriver.Shader, error) { - vs, ps, offsets := hlsl.Compile(program) - vsh, psh, err := compileShader(vs, ps) + vsh, psh, offsets, err := compileShader(program) if err != nil { return nil, err } diff --git a/internal/graphicsdriver/directx/shader_windows.go b/internal/graphicsdriver/directx/shader_windows.go index 3c3b7509a..cd197e28e 100644 --- a/internal/graphicsdriver/directx/shader_windows.go +++ b/internal/graphicsdriver/directx/shader_windows.go @@ -21,11 +21,13 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/shaderir" + "github.com/hajimehoshi/ebiten/v2/internal/shaderir/hlsl" ) var vertexShaderCache = map[string]*_ID3DBlob{} -func compileShader(vs, ps string) (vsh, psh *_ID3DBlob, ferr error) { +func compileShader(program *shaderir.Program) (vsh, psh *_ID3DBlob, uniformOffsets []int, ferr error) { + vs, ps, offsets := hlsl.Compile(program) var flag uint32 = uint32(_D3DCOMPILE_OPTIMIZATION_LEVEL3) defer func() { @@ -74,10 +76,10 @@ func compileShader(vs, ps string) (vsh, psh *_ID3DBlob, ferr error) { }) if err := wg.Wait(); err != nil { - return nil, nil, err + return nil, nil, nil, err } - return + return vsh, psh, offsets, nil } func constantBufferSize(uniformTypes []shaderir.Type, uniformOffsets []int) int { From 5d4a68b0ea9e4a3386df74c06a3ed8585a293913 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 5 May 2024 20:47:35 +0900 Subject: [PATCH 41/81] internal/shaderir/hlsl: refactoring: separate calculation uniform offsets --- internal/graphicsdriver/directx/graphics11_windows.go | 5 +++-- internal/graphicsdriver/directx/graphics12_windows.go | 5 +++-- internal/graphicsdriver/directx/shader_windows.go | 8 ++++---- internal/shader/shader_test.go | 2 +- internal/shaderir/hlsl/hlsl.go | 4 ++-- internal/shaderir/hlsl/packing.go | 4 ++-- 6 files changed, 15 insertions(+), 13 deletions(-) diff --git a/internal/graphicsdriver/directx/graphics11_windows.go b/internal/graphicsdriver/directx/graphics11_windows.go index 53035b2f6..e77b0cfe0 100644 --- a/internal/graphicsdriver/directx/graphics11_windows.go +++ b/internal/graphicsdriver/directx/graphics11_windows.go @@ -24,6 +24,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/shaderir" + "github.com/hajimehoshi/ebiten/v2/internal/shaderir/hlsl" ) var inputElementDescsForDX11 = []_D3D11_INPUT_ELEMENT_DESC{ @@ -481,7 +482,7 @@ func (g *graphics11) MaxImageSize() int { } func (g *graphics11) NewShader(program *shaderir.Program) (graphicsdriver.Shader, error) { - vsh, psh, offsets, err := compileShader(program) + vsh, psh, err := compileShader(program) if err != nil { return nil, err } @@ -490,7 +491,7 @@ func (g *graphics11) NewShader(program *shaderir.Program) (graphicsdriver.Shader graphics: g, id: g.genNextShaderID(), uniformTypes: program.Uniforms, - uniformOffsets: offsets, + uniformOffsets: hlsl.CalcUniformMemoryOffsets(program), vertexShaderBlob: vsh, pixelShaderBlob: psh, } diff --git a/internal/graphicsdriver/directx/graphics12_windows.go b/internal/graphicsdriver/directx/graphics12_windows.go index dae2f9c7f..634b5c06d 100644 --- a/internal/graphicsdriver/directx/graphics12_windows.go +++ b/internal/graphicsdriver/directx/graphics12_windows.go @@ -25,6 +25,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/microsoftgdk" "github.com/hajimehoshi/ebiten/v2/internal/shaderir" + "github.com/hajimehoshi/ebiten/v2/internal/shaderir/hlsl" ) type resourceWithSize struct { @@ -1063,7 +1064,7 @@ func (g *graphics12) MaxImageSize() int { } func (g *graphics12) NewShader(program *shaderir.Program) (graphicsdriver.Shader, error) { - vsh, psh, offsets, err := compileShader(program) + vsh, psh, err := compileShader(program) if err != nil { return nil, err } @@ -1072,7 +1073,7 @@ func (g *graphics12) NewShader(program *shaderir.Program) (graphicsdriver.Shader graphics: g, id: g.genNextShaderID(), uniformTypes: program.Uniforms, - uniformOffsets: offsets, + uniformOffsets: hlsl.CalcUniformMemoryOffsets(program), vertexShader: vsh, pixelShader: psh, } diff --git a/internal/graphicsdriver/directx/shader_windows.go b/internal/graphicsdriver/directx/shader_windows.go index cd197e28e..a8003b7a2 100644 --- a/internal/graphicsdriver/directx/shader_windows.go +++ b/internal/graphicsdriver/directx/shader_windows.go @@ -26,8 +26,8 @@ import ( var vertexShaderCache = map[string]*_ID3DBlob{} -func compileShader(program *shaderir.Program) (vsh, psh *_ID3DBlob, uniformOffsets []int, ferr error) { - vs, ps, offsets := hlsl.Compile(program) +func compileShader(program *shaderir.Program) (vsh, psh *_ID3DBlob, ferr error) { + vs, ps := hlsl.Compile(program) var flag uint32 = uint32(_D3DCOMPILE_OPTIMIZATION_LEVEL3) defer func() { @@ -76,10 +76,10 @@ func compileShader(program *shaderir.Program) (vsh, psh *_ID3DBlob, uniformOffse }) if err := wg.Wait(); err != nil { - return nil, nil, nil, err + return nil, nil, err } - return vsh, psh, offsets, nil + return vsh, psh, nil } func constantBufferSize(uniformTypes []shaderir.Type, uniformOffsets []int) int { diff --git a/internal/shader/shader_test.go b/internal/shader/shader_test.go index fd4abaee1..954ba099d 100644 --- a/internal/shader/shader_test.go +++ b/internal/shader/shader_test.go @@ -188,7 +188,7 @@ func TestCompile(t *testing.T) { } if tc.HLSL != nil { - vs, _, _ := hlsl.Compile(s) + vs, _ := hlsl.Compile(s) if got, want := hlslNormalize(vs), hlslNormalize(string(tc.HLSL)); got != want { compare(t, "HLSL", got, want) } diff --git a/internal/shaderir/hlsl/hlsl.go b/internal/shaderir/hlsl/hlsl.go index ca43cad45..22a05e635 100644 --- a/internal/shaderir/hlsl/hlsl.go +++ b/internal/shaderir/hlsl/hlsl.go @@ -86,8 +86,8 @@ float4x4 float4x4FromScalar(float x) { return float4x4(x, 0, 0, 0, 0, x, 0, 0, 0, 0, x, 0, 0, 0, 0, x); }` -func Compile(p *shaderir.Program) (vertexShader, pixelShader string, offsets []int) { - offsets = calculateMemoryOffsets(p.Uniforms) +func Compile(p *shaderir.Program) (vertexShader, pixelShader string) { + offsets := CalcUniformMemoryOffsets(p) c := &compileContext{ unit: p.Unit, diff --git a/internal/shaderir/hlsl/packing.go b/internal/shaderir/hlsl/packing.go index ee28d01ee..217a12576 100644 --- a/internal/shaderir/hlsl/packing.go +++ b/internal/shaderir/hlsl/packing.go @@ -22,7 +22,7 @@ import ( const boundaryInBytes = 16 -func calculateMemoryOffsets(uniforms []shaderir.Type) []int { +func CalcUniformMemoryOffsets(program *shaderir.Program) []int { // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-packing-rules // https://github.com/microsoft/DirectXShaderCompiler/wiki/Buffer-Packing @@ -38,7 +38,7 @@ func calculateMemoryOffsets(uniforms []shaderir.Type) []int { // TODO: Reorder the variables with packoffset. // See https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-variable-packoffset - for _, u := range uniforms { + for _, u := range program.Uniforms { switch u.Main { case shaderir.Float: offsets = append(offsets, head) From 10d96601250f34a9e7d0ba5691b04edb230c0d3b Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 5 May 2024 16:52:10 +0900 Subject: [PATCH 42/81] shaderprecomp: implement for Windows Closes #2861 --- .gitignore | 2 + examples/shaderprecomp/fxc/dummy.fxc | 1 + examples/shaderprecomp/fxc/gen.go | 130 ++++++++++++++++++ examples/shaderprecomp/fxc/generate.go | 17 +++ examples/shaderprecomp/main.go | 1 + examples/shaderprecomp/register_others.go | 2 +- examples/shaderprecomp/register_windows.go | 65 +++++++++ .../graphicsdriver/directx/d3d_windows.go | 17 ++- .../graphicsdriver/directx/shader_windows.go | 73 +++++++++- shaderprecomp/shaderprecomp_windows.go | 65 +++++++++ 10 files changed, 366 insertions(+), 7 deletions(-) create mode 100644 examples/shaderprecomp/fxc/dummy.fxc create mode 100644 examples/shaderprecomp/fxc/gen.go create mode 100644 examples/shaderprecomp/fxc/generate.go create mode 100644 examples/shaderprecomp/register_windows.go create mode 100644 shaderprecomp/shaderprecomp_windows.go diff --git a/.gitignore b/.gitignore index 719c6998d..981311b14 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,7 @@ go.work go.work.sum +*.fxc +!dummy.fxc *.metallib !dummy.metallib diff --git a/examples/shaderprecomp/fxc/dummy.fxc b/examples/shaderprecomp/fxc/dummy.fxc new file mode 100644 index 000000000..2a3f3dd8e --- /dev/null +++ b/examples/shaderprecomp/fxc/dummy.fxc @@ -0,0 +1 @@ +This is a dummy .fxc file to trick Go's embed package. diff --git a/examples/shaderprecomp/fxc/gen.go b/examples/shaderprecomp/fxc/gen.go new file mode 100644 index 000000000..8d8215069 --- /dev/null +++ b/examples/shaderprecomp/fxc/gen.go @@ -0,0 +1,130 @@ +// Copyright 2024 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build ignore + +// This is a program to generate precompiled HLSL blobs (FXC files). +// +// See https://learn.microsoft.com/en-us/windows/win32/direct3dtools/fxc. +package main + +import ( + "errors" + "fmt" + "os" + "os/exec" + "path/filepath" + + "github.com/hajimehoshi/ebiten/v2/shaderprecomp" +) + +func main() { + if err := run(); err != nil { + panic(err) + } +} + +func run() error { + if _, err := exec.LookPath("fxc.exe"); err != nil { + if errors.Is(err, exec.ErrNotFound) { + fmt.Fprintln(os.Stderr, "fxc.exe not found. Please install Windows SDK.") + fmt.Fprintln(os.Stderr, "See https://learn.microsoft.com/en-us/windows/win32/direct3dtools/fxc for more details.") + fmt.Fprintln(os.Stderr, "On PowerShell, you can add a path to the PATH environment variable temporarily like:") + fmt.Fprintln(os.Stderr) + fmt.Fprintln(os.Stderr, ` & (Get-Process -Id $PID).Path { $env:PATH="C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64;"+$env:PATH; go generate .\examples\shaderprecomp\fxc\ }`) + fmt.Fprintln(os.Stderr) + os.Exit(1) + } + return err + } + + tmpdir, err := os.MkdirTemp("", "") + if err != nil { + return err + } + defer os.RemoveAll(tmpdir) + + srcs := shaderprecomp.AppendBuildinShaderSources(nil) + + defaultSrcBytes, err := os.ReadFile(filepath.Join("..", "defaultshader.go")) + if err != nil { + return err + } + defaultSrc, err := shaderprecomp.NewShaderSource(defaultSrcBytes) + if err != nil { + return err + } + srcs = append(srcs, defaultSrc) + + for _, src := range srcs { + // Compiling sources in parallel causes a mixed error message on the console. + if err := compile(src, tmpdir); err != nil { + return err + } + } + return nil +} + +func generateHSLSFiles(source *shaderprecomp.ShaderSource, tmpdir string) (vs, ps string, err error) { + id := source.ID().String() + + vsHLSLFilePath := filepath.Join(tmpdir, id+"_vs.hlsl") + vsf, err := os.Create(vsHLSLFilePath) + if err != nil { + return "", "", err + } + defer vsf.Close() + + psHLSLFilePath := filepath.Join(tmpdir, id+"_ps.hlsl") + psf, err := os.Create(psHLSLFilePath) + if err != nil { + return "", "", err + } + defer psf.Close() + + if err := shaderprecomp.CompileToHLSL(vsf, psf, source); err != nil { + return "", "", err + } + + return vsHLSLFilePath, psHLSLFilePath, nil +} + +func compile(source *shaderprecomp.ShaderSource, tmpdir string) error { + // Generate HLSL files. Make sure this process doesn't have any handlers of the files. + // Without closing the files, fxc.exe cannot access the files. + vsHLSLFilePath, psHLSLFilePath, err := generateHSLSFiles(source, tmpdir) + if err != nil { + return err + } + + id := source.ID().String() + + vsFXCFilePath := id + "_vs.fxc" + cmd := exec.Command("fxc.exe", "/nologo", "/O3", "/T", shaderprecomp.HLSLVertexShaderProfile, "/E", shaderprecomp.HLSLVertexShaderEntryPoint, "/Fo", vsFXCFilePath, vsHLSLFilePath) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return err + } + + psFXCFilePath := id + "_ps.fxc" + cmd = exec.Command("fxc.exe", "/nologo", "/O3", "/T", shaderprecomp.HLSLPixelShaderProfile, "/E", shaderprecomp.HLSLPixelShaderEntryPoint, "/Fo", psFXCFilePath, psHLSLFilePath) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return err + } + + return nil +} diff --git a/examples/shaderprecomp/fxc/generate.go b/examples/shaderprecomp/fxc/generate.go new file mode 100644 index 000000000..772999805 --- /dev/null +++ b/examples/shaderprecomp/fxc/generate.go @@ -0,0 +1,17 @@ +// Copyright 2024 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:generate go run gen.go + +package fxc diff --git a/examples/shaderprecomp/main.go b/examples/shaderprecomp/main.go index 971d8aa04..60d85edce 100644 --- a/examples/shaderprecomp/main.go +++ b/examples/shaderprecomp/main.go @@ -67,6 +67,7 @@ func main() { if err := registerPrecompiledShaders(); err != nil { log.Fatal(err) } + ebiten.SetWindowTitle("Ebitengine Example (Shader Precompilation)") if err := ebiten.RunGame(&Game{}); err != nil { log.Fatal(err) } diff --git a/examples/shaderprecomp/register_others.go b/examples/shaderprecomp/register_others.go index 2f9c757ef..b0e37ea6f 100644 --- a/examples/shaderprecomp/register_others.go +++ b/examples/shaderprecomp/register_others.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build !darwin +//go:build !darwin && !windows package main diff --git a/examples/shaderprecomp/register_windows.go b/examples/shaderprecomp/register_windows.go new file mode 100644 index 000000000..34ac7437a --- /dev/null +++ b/examples/shaderprecomp/register_windows.go @@ -0,0 +1,65 @@ +// Copyright 2024 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "embed" + "errors" + "fmt" + "io/fs" + "os" + + "github.com/hajimehoshi/ebiten/v2/shaderprecomp" +) + +// https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/ + +//go:embed fxc/*.fxc +var fxcs embed.FS + +func registerPrecompiledShaders() error { + srcs := shaderprecomp.AppendBuildinShaderSources(nil) + defaultShaderSource, err := shaderprecomp.NewShaderSource(defaultShaderSourceBytes) + if err != nil { + return err + } + srcs = append(srcs, defaultShaderSource) + + for _, src := range srcs { + vsname := src.ID().String() + "_vs.fxc" + vs, err := fxcs.ReadFile("fxc/" + vsname) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + fmt.Fprintf(os.Stderr, "precompiled HLSL library %s was not found. Run 'go generate' for 'fxc' directory to generate them.\n", vsname) + continue + } + return err + } + + psname := src.ID().String() + "_ps.fxc" + ps, err := fxcs.ReadFile("fxc/" + psname) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + fmt.Fprintf(os.Stderr, "precompiled HLSL library %s was not found. Run 'go generate' for 'fxc' directory to generate them.\n", psname) + continue + } + return err + } + + shaderprecomp.RegisterFXCs(src, vs, ps) + } + + return nil +} diff --git a/internal/graphicsdriver/directx/d3d_windows.go b/internal/graphicsdriver/directx/d3d_windows.go index be90829a1..845bbda1c 100644 --- a/internal/graphicsdriver/directx/d3d_windows.go +++ b/internal/graphicsdriver/directx/d3d_windows.go @@ -71,7 +71,8 @@ const ( ) var ( - procD3DCompile *windows.LazyProc + procD3DCompile *windows.LazyProc + procD3DCreateBlob *windows.LazyProc ) func init() { @@ -93,6 +94,7 @@ func init() { } procD3DCompile = d3dcompiler.NewProc("D3DCompile") + procD3DCreateBlob = d3dcompiler.NewProc("D3DCreateBlob") } func isD3DCompilerDLLAvailable() bool { @@ -135,6 +137,19 @@ func _D3DCompile(srcData []byte, sourceName string, pDefines []_D3D_SHADER_MACRO return code, nil } +func _D3DCreateBlob(size uint) (*_ID3DBlob, error) { + if !isD3DCompilerDLLAvailable() { + return nil, fmt.Errorf("directx: d3dcompiler_*.dll is missing in this environment") + } + + var blob *_ID3DBlob + r, _, _ := procD3DCreateBlob.Call(uintptr(size), uintptr(unsafe.Pointer(&blob))) + if uint32(r) != uint32(windows.S_OK) { + return nil, fmt.Errorf("directx: D3DCreateBlob failed: %w", handleError(windows.Handle(uint32(r)))) + } + return blob, nil +} + type _D3D_SHADER_MACRO struct { Name *byte Definition *byte diff --git a/internal/graphicsdriver/directx/shader_windows.go b/internal/graphicsdriver/directx/shader_windows.go index a8003b7a2..172ec1638 100644 --- a/internal/graphicsdriver/directx/shader_windows.go +++ b/internal/graphicsdriver/directx/shader_windows.go @@ -16,6 +16,8 @@ package directx import ( "fmt" + "sync" + "unsafe" "golang.org/x/sync/errgroup" @@ -24,12 +26,57 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/shaderir/hlsl" ) +const ( + VertexShaderProfile = "vs_4_0" + PixelShaderProfile = "ps_4_0" + + VertexShaderEntryPoint = "VSMain" + PixelShaderEntryPoint = "PSMain" +) + +type fxcPair struct { + vertex []byte + pixel []byte +} + +type precompiledFXCs struct { + binaries map[shaderir.SourceHash]fxcPair + m sync.Mutex +} + +func (c *precompiledFXCs) put(hash shaderir.SourceHash, vertex, pixel []byte) { + c.m.Lock() + defer c.m.Unlock() + + if c.binaries == nil { + c.binaries = map[shaderir.SourceHash]fxcPair{} + } + if _, ok := c.binaries[hash]; ok { + panic(fmt.Sprintf("directx: the precompiled library for the hash %s is already registered", hash.String())) + } + c.binaries[hash] = fxcPair{ + vertex: vertex, + pixel: pixel, + } +} + +func (c *precompiledFXCs) get(hash shaderir.SourceHash) ([]byte, []byte) { + c.m.Lock() + defer c.m.Unlock() + + f := c.binaries[hash] + return f.vertex, f.pixel +} + +var thePrecompiledFXCs precompiledFXCs + +func RegisterPrecompiledFXCs(hash shaderir.SourceHash, vertex, pixel []byte) { + thePrecompiledFXCs.put(hash, vertex, pixel) +} + var vertexShaderCache = map[string]*_ID3DBlob{} func compileShader(program *shaderir.Program) (vsh, psh *_ID3DBlob, ferr error) { - vs, ps := hlsl.Compile(program) - var flag uint32 = uint32(_D3DCOMPILE_OPTIMIZATION_LEVEL3) - defer func() { if ferr == nil { return @@ -42,6 +89,22 @@ func compileShader(program *shaderir.Program) (vsh, psh *_ID3DBlob, ferr error) } }() + if vshBin, pshBin := thePrecompiledFXCs.get(program.SourceHash); vshBin != nil && pshBin != nil { + var err error + if vsh, err = _D3DCreateBlob(uint(len(vshBin))); err != nil { + return nil, nil, err + } + if psh, err = _D3DCreateBlob(uint(len(pshBin))); err != nil { + return nil, nil, err + } + copy(unsafe.Slice((*byte)(vsh.GetBufferPointer()), vsh.GetBufferSize()), vshBin) + copy(unsafe.Slice((*byte)(psh.GetBufferPointer()), psh.GetBufferSize()), pshBin) + return vsh, psh, nil + } + + vs, ps := hlsl.Compile(program) + var flag uint32 = uint32(_D3DCOMPILE_OPTIMIZATION_LEVEL3) + var wg errgroup.Group // Vertex shaders are likely the same. If so, reuse the same _ID3DBlob. @@ -58,7 +121,7 @@ func compileShader(program *shaderir.Program) (vsh, psh *_ID3DBlob, ferr error) } }() wg.Go(func() error { - v, err := _D3DCompile([]byte(vs), "shader", nil, nil, "VSMain", "vs_4_0", flag, 0) + v, err := _D3DCompile([]byte(vs), "shader", nil, nil, VertexShaderEntryPoint, VertexShaderProfile, flag, 0) if err != nil { return fmt.Errorf("directx: D3DCompile for VSMain failed, original source: %s, %w", vs, err) } @@ -67,7 +130,7 @@ func compileShader(program *shaderir.Program) (vsh, psh *_ID3DBlob, ferr error) }) } wg.Go(func() error { - p, err := _D3DCompile([]byte(ps), "shader", nil, nil, "PSMain", "ps_4_0", flag, 0) + p, err := _D3DCompile([]byte(ps), "shader", nil, nil, PixelShaderEntryPoint, PixelShaderProfile, flag, 0) if err != nil { return fmt.Errorf("directx: D3DCompile for PSMain failed, original source: %s, %w", ps, err) } diff --git a/shaderprecomp/shaderprecomp_windows.go b/shaderprecomp/shaderprecomp_windows.go new file mode 100644 index 000000000..f1b96bdbb --- /dev/null +++ b/shaderprecomp/shaderprecomp_windows.go @@ -0,0 +1,65 @@ +// Copyright 2024 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package shaderprecomp + +import ( + "io" + + "github.com/hajimehoshi/ebiten/v2/internal/graphics" + "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/directx" + "github.com/hajimehoshi/ebiten/v2/internal/shaderir" + "github.com/hajimehoshi/ebiten/v2/internal/shaderir/hlsl" +) + +const ( + // HLSLVertexShaderProfile is the target profile for vertex shaders. + HLSLVertexShaderProfile = directx.VertexShaderProfile + + // HLSLPixelShaderProfile is the target profile for pixel shaders. + HLSLPixelShaderProfile = directx.PixelShaderProfile + + // HLSLVertexShaderEntryPoint is the entry point name for vertex shaders. + HLSLVertexShaderEntryPoint = directx.VertexShaderEntryPoint + + // HLSLPixelShaderEntryPoint is the entry point name for pixel shaders. + HLSLPixelShaderEntryPoint = directx.PixelShaderEntryPoint +) + +// CompileToHLSL compiles the shader source to High-Level Shader Language to writers. +// +// CompileToHLSL is concurrent-safe. +func CompileToHLSL(vertexWriter, pixelWriter io.Writer, source *ShaderSource) error { + ir, err := graphics.CompileShader(source.source) + if err != nil { + return err + } + vs, ps := hlsl.Compile(ir) + if _, err = vertexWriter.Write([]byte(vs)); err != nil { + return err + } + if _, err = pixelWriter.Write([]byte(ps)); err != nil { + return err + } + return nil +} + +// RegisterFXCs registers a precompiled HLSL (FXC) for a shader source. +// vertexFXC and pixelFXC must be the content of .fxc files generated by `fxc` command. +// For more details, see https://learn.microsoft.com/en-us/windows/win32/direct3dtools/dx-graphics-tools-fxc-using. +// +// RegisterFXCs is concurrent-safe. +func RegisterFXCs(source *ShaderSource, vertexFXC, pixelFXC []byte) { + directx.RegisterPrecompiledFXCs(shaderir.SourceHash(source.ID()), vertexFXC, pixelFXC) +} From a391da6c77346078c79168b8aba39844dcde9130 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 6 May 2024 17:25:21 +0900 Subject: [PATCH 43/81] examples/shaderprecomp/metallib: stop using errgroup --- examples/shaderprecomp/fxc/gen.go | 1 + examples/shaderprecomp/metallib/gen.go | 15 +++++---------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/examples/shaderprecomp/fxc/gen.go b/examples/shaderprecomp/fxc/gen.go index 8d8215069..7a6457469 100644 --- a/examples/shaderprecomp/fxc/gen.go +++ b/examples/shaderprecomp/fxc/gen.go @@ -68,6 +68,7 @@ func run() error { srcs = append(srcs, defaultSrc) for _, src := range srcs { + // Avoid using errgroup.Group. // Compiling sources in parallel causes a mixed error message on the console. if err := compile(src, tmpdir); err != nil { return err diff --git a/examples/shaderprecomp/metallib/gen.go b/examples/shaderprecomp/metallib/gen.go index b15413185..1804c9da9 100644 --- a/examples/shaderprecomp/metallib/gen.go +++ b/examples/shaderprecomp/metallib/gen.go @@ -24,8 +24,6 @@ import ( "os/exec" "path/filepath" - "golang.org/x/sync/errgroup" - "github.com/hajimehoshi/ebiten/v2/shaderprecomp" ) @@ -54,15 +52,12 @@ func run() error { } srcs = append(srcs, defaultSrc) - var wg errgroup.Group for _, src := range srcs { - source := src - wg.Go(func() error { - return compile(source, tmpdir) - }) - } - if err := wg.Wait(); err != nil { - return err + // Avoid using errgroup.Group. + // Compiling sources in parallel causes a mixed error message on the console. + if err := compile(src, tmpdir); err != nil { + return err + } } return nil } From 2261cf76de1b9fc8e858cdc807da6e99e50e8c96 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 6 May 2024 19:36:16 +0900 Subject: [PATCH 44/81] internal/graphicsdriver/metal/ca: follow ObjC convention Updates #2981 --- internal/graphicsdriver/metal/ca/ca_darwin.go | 36 +++++++++---------- internal/graphicsdriver/metal/view_darwin.go | 2 +- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/internal/graphicsdriver/metal/ca/ca_darwin.go b/internal/graphicsdriver/metal/ca/ca_darwin.go index 528dba204..8c5bce768 100644 --- a/internal/graphicsdriver/metal/ca/ca_darwin.go +++ b/internal/graphicsdriver/metal/ca/ca_darwin.go @@ -35,7 +35,7 @@ import ( // Layer is an object that manages image-based content and // allows you to perform animations on that content. // -// Reference: https://developer.apple.com/documentation/quartzcore/calayer. +// Reference: https://developer.apple.com/documentation/quartzcore/calayer?language=objc. type Layer interface { // Layer returns the underlying CALayer * pointer. Layer() unsafe.Pointer @@ -43,15 +43,15 @@ type Layer interface { // MetalLayer is a Core Animation Metal layer, a layer that manages a pool of Metal drawables. // -// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer. +// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer?language=objc. type MetalLayer struct { metalLayer objc.ID } -// MakeMetalLayer creates a new Core Animation Metal layer. +// NewMetalLayer creates a new Core Animation Metal layer. // -// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer. -func MakeMetalLayer() (MetalLayer, error) { +// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer?language=objc. +func NewMetalLayer() (MetalLayer, error) { coreGraphics, err := purego.Dlopen("/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics", purego.RTLD_LAZY|purego.RTLD_GLOBAL) if err != nil { return MetalLayer{}, err @@ -88,14 +88,14 @@ func (ml MetalLayer) Layer() unsafe.Pointer { // PixelFormat returns the pixel format of textures for rendering layer content. // -// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478155-pixelformat. +// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478155-pixelformat?language=objc. func (ml MetalLayer) PixelFormat() mtl.PixelFormat { return mtl.PixelFormat(ml.metalLayer.Send(objc.RegisterName("pixelFormat"))) } // SetDevice sets the Metal device responsible for the layer's drawable resources. // -// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478163-device. +// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478163-device?language=objc. func (ml MetalLayer) SetDevice(device mtl.Device) { ml.metalLayer.Send(objc.RegisterName("setDevice:"), uintptr(device.Device())) } @@ -111,7 +111,7 @@ func (ml MetalLayer) SetOpaque(opaque bool) { // PixelFormatRGBA16Float, PixelFormatBGRA10XR, or PixelFormatBGRA10XRSRGB. // SetPixelFormat panics for other values. // -// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478155-pixelformat. +// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478155-pixelformat?language=objc. func (ml MetalLayer) SetPixelFormat(pf mtl.PixelFormat) { switch pf { case mtl.PixelFormatRGBA8UNorm, mtl.PixelFormatRGBA8UNormSRGB, mtl.PixelFormatBGRA8UNorm, mtl.PixelFormatBGRA8UNormSRGB, mtl.PixelFormatStencil8: @@ -126,7 +126,7 @@ func (ml MetalLayer) SetPixelFormat(pf mtl.PixelFormat) { // // It can set to 2 or 3 only. SetMaximumDrawableCount panics for other values. // -// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/2938720-maximumdrawablecount. +// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/2938720-maximumdrawablecount?language=objc. func (ml MetalLayer) SetMaximumDrawableCount(count int) { if count < 2 || count > 3 { panic(errors.New(fmt.Sprintf("failed trying to set maximumDrawableCount to %d outside of the valid range of [2, 3]", count))) @@ -137,7 +137,7 @@ func (ml MetalLayer) SetMaximumDrawableCount(count int) { // SetDisplaySyncEnabled controls whether the Metal layer and its drawables // are synchronized with the display's refresh rate. // -// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/2887087-displaysyncenabled. +// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/2887087-displaysyncenabled?language=objc. func (ml MetalLayer) SetDisplaySyncEnabled(enabled bool) { if runtime.GOOS == "ios" { return @@ -147,7 +147,7 @@ func (ml MetalLayer) SetDisplaySyncEnabled(enabled bool) { // SetDrawableSize sets the size, in pixels, of textures for rendering layer content. // -// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478174-drawablesize. +// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478174-drawablesize?language=objc. func (ml MetalLayer) SetDrawableSize(width, height int) { // TODO: once objc supports calling functions with struct arguments replace this with just a ID.Send call var sel_setDrawableSize = objc.RegisterName("setDrawableSize:") @@ -161,7 +161,7 @@ func (ml MetalLayer) SetDrawableSize(width, height int) { // NextDrawable returns a Metal drawable. // -// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478172-nextdrawable. +// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478172-nextdrawable?language=objc. func (ml MetalLayer) NextDrawable() (MetalDrawable, error) { md := ml.metalLayer.Send(objc.RegisterName("nextDrawable")) if md == 0 { @@ -172,28 +172,28 @@ func (ml MetalLayer) NextDrawable() (MetalDrawable, error) { // PresentsWithTransaction returns a Boolean value that determines whether the layer presents its content using a Core Animation transaction. // -// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478157-presentswithtransaction +// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478157-presentswithtransaction?language=objc func (ml MetalLayer) PresentsWithTransaction() bool { return ml.metalLayer.Send(objc.RegisterName("presentsWithTransaction")) != 0 } // SetPresentsWithTransaction sets a Boolean value that determines whether the layer presents its content using a Core Animation transaction. // -// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478157-presentswithtransaction +// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478157-presentswithtransaction?language=objc func (ml MetalLayer) SetPresentsWithTransaction(presentsWithTransaction bool) { ml.metalLayer.Send(objc.RegisterName("setPresentsWithTransaction:"), presentsWithTransaction) } // SetFramebufferOnly sets a Boolean value that determines whether the layer’s textures are used only for rendering. // -// https://developer.apple.com/documentation/quartzcore/cametallayer/1478168-framebufferonly +// https://developer.apple.com/documentation/quartzcore/cametallayer/1478168-framebufferonly?language=objc func (ml MetalLayer) SetFramebufferOnly(framebufferOnly bool) { ml.metalLayer.Send(objc.RegisterName("setFramebufferOnly:"), framebufferOnly) } // MetalDrawable is a displayable resource that can be rendered or written to by Metal. // -// Reference: https://developer.apple.com/documentation/quartzcore/cametaldrawable. +// Reference: https://developer.apple.com/documentation/quartzcore/cametaldrawable?language=objc. type MetalDrawable struct { metalDrawable objc.ID } @@ -205,14 +205,14 @@ func (md MetalDrawable) Drawable() unsafe.Pointer { // Texture returns a Metal texture object representing the drawable object's content. // -// Reference: https://developer.apple.com/documentation/quartzcore/cametaldrawable/1478159-texture. +// Reference: https://developer.apple.com/documentation/quartzcore/cametaldrawable/1478159-texture?language=objc. func (md MetalDrawable) Texture() mtl.Texture { return mtl.NewTexture(md.metalDrawable.Send(objc.RegisterName("texture"))) } // Present presents the drawable onscreen as soon as possible. // -// Reference: https://developer.apple.com/documentation/metal/mtldrawable/1470284-present. +// Reference: https://developer.apple.com/documentation/metal/mtldrawable/1470284-present?language=objc. func (md MetalDrawable) Present() { md.metalDrawable.Send(objc.RegisterName("present")) } diff --git a/internal/graphicsdriver/metal/view_darwin.go b/internal/graphicsdriver/metal/view_darwin.go index b22047fbf..d45690b81 100644 --- a/internal/graphicsdriver/metal/view_darwin.go +++ b/internal/graphicsdriver/metal/view_darwin.go @@ -61,7 +61,7 @@ func (v *view) colorPixelFormat() mtl.PixelFormat { func (v *view) initialize(device mtl.Device) error { v.device = device - ml, err := ca.MakeMetalLayer() + ml, err := ca.NewMetalLayer() if err != nil { return err } From f0ca3f1870e4b21fe927a88772f146ca1fe693fb Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 6 May 2024 21:38:59 +0900 Subject: [PATCH 45/81] internal/graphicsdriver/metal/mtl: follow ObjC convention Closes #2981 --- .../graphicsdriver/metal/graphics_darwin.go | 32 +-- .../metal/mtl/example_darwin_test.go | 20 +- .../graphicsdriver/metal/mtl/mtl_darwin.go | 202 +++++++++--------- .../metal/mtl/mtl_darwin_test.go | 20 +- .../graphicsdriver/metal/shader_darwin.go | 10 +- 5 files changed, 140 insertions(+), 144 deletions(-) diff --git a/internal/graphicsdriver/metal/graphics_darwin.go b/internal/graphicsdriver/metal/graphics_darwin.go index 2d7c2948a..1ea2bd50b 100644 --- a/internal/graphicsdriver/metal/graphics_darwin.go +++ b/internal/graphicsdriver/metal/graphics_darwin.go @@ -184,7 +184,7 @@ func (g *Graphics) gcBuffers() { func (g *Graphics) availableBuffer(length uintptr) mtl.Buffer { if g.cb == (mtl.CommandBuffer{}) { - g.cb = g.cq.MakeCommandBuffer() + g.cb = g.cq.CommandBuffer() } var newBuf mtl.Buffer @@ -197,7 +197,7 @@ func (g *Graphics) availableBuffer(length uintptr) mtl.Buffer { } if newBuf == (mtl.Buffer{}) { - newBuf = g.view.getMTLDevice().MakeBufferWithLength(pow2(length), resourceStorageMode) + newBuf = g.view.getMTLDevice().NewBufferWithLength(pow2(length), resourceStorageMode) } if g.buffers == nil { @@ -286,7 +286,7 @@ func (g *Graphics) NewImage(width, height int) (graphicsdriver.Image, error) { StorageMode: storageMode, Usage: mtl.TextureUsageShaderRead | mtl.TextureUsageRenderTarget, } - t := g.view.getMTLDevice().MakeTexture(td) + t := g.view.getMTLDevice().NewTextureWithDescriptor(td) i := &Image{ id: g.genNextImageID(), graphics: g, @@ -397,7 +397,7 @@ func (g *Graphics) Initialize() error { } // The stencil reference value is always 0 (default). - g.dsss[noStencil] = g.view.getMTLDevice().MakeDepthStencilState(mtl.DepthStencilDescriptor{ + g.dsss[noStencil] = g.view.getMTLDevice().NewDepthStencilStateWithDescriptor(mtl.DepthStencilDescriptor{ BackFaceStencil: mtl.StencilDescriptor{ StencilFailureOperation: mtl.StencilOperationKeep, DepthFailureOperation: mtl.StencilOperationKeep, @@ -411,7 +411,7 @@ func (g *Graphics) Initialize() error { StencilCompareFunction: mtl.CompareFunctionAlways, }, }) - g.dsss[incrementStencil] = g.view.getMTLDevice().MakeDepthStencilState(mtl.DepthStencilDescriptor{ + g.dsss[incrementStencil] = g.view.getMTLDevice().NewDepthStencilStateWithDescriptor(mtl.DepthStencilDescriptor{ BackFaceStencil: mtl.StencilDescriptor{ StencilFailureOperation: mtl.StencilOperationKeep, DepthFailureOperation: mtl.StencilOperationKeep, @@ -425,7 +425,7 @@ func (g *Graphics) Initialize() error { StencilCompareFunction: mtl.CompareFunctionAlways, }, }) - g.dsss[invertStencil] = g.view.getMTLDevice().MakeDepthStencilState(mtl.DepthStencilDescriptor{ + g.dsss[invertStencil] = g.view.getMTLDevice().NewDepthStencilStateWithDescriptor(mtl.DepthStencilDescriptor{ BackFaceStencil: mtl.StencilDescriptor{ StencilFailureOperation: mtl.StencilOperationKeep, DepthFailureOperation: mtl.StencilOperationKeep, @@ -439,7 +439,7 @@ func (g *Graphics) Initialize() error { StencilCompareFunction: mtl.CompareFunctionAlways, }, }) - g.dsss[drawWithStencil] = g.view.getMTLDevice().MakeDepthStencilState(mtl.DepthStencilDescriptor{ + g.dsss[drawWithStencil] = g.view.getMTLDevice().NewDepthStencilStateWithDescriptor(mtl.DepthStencilDescriptor{ BackFaceStencil: mtl.StencilDescriptor{ StencilFailureOperation: mtl.StencilOperationKeep, DepthFailureOperation: mtl.StencilOperationKeep, @@ -454,7 +454,7 @@ func (g *Graphics) Initialize() error { }, }) - g.cq = g.view.getMTLDevice().MakeCommandQueue() + g.cq = g.view.getMTLDevice().NewCommandQueue() return nil } @@ -505,9 +505,9 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs } if g.cb == (mtl.CommandBuffer{}) { - g.cb = g.cq.MakeCommandBuffer() + g.cb = g.cq.CommandBuffer() } - g.rce = g.cb.MakeRenderCommandEncoder(rpd) + g.rce = g.cb.RenderCommandEncoderWithDescriptor(rpd) } w, h := dst.internalSize() @@ -810,8 +810,8 @@ func (i *Image) syncTexture() { panic("metal: command buffer must be empty at syncTexture: flushIfNeeded is not called yet?") } - cb := i.graphics.cq.MakeCommandBuffer() - bce := cb.MakeBlitCommandEncoder() + cb := i.graphics.cq.CommandBuffer() + bce := cb.BlitCommandEncoder() bce.SynchronizeTexture(i.texture, 0, 0) bce.EndEncoding() @@ -859,7 +859,7 @@ func (i *Image) WritePixels(args []graphicsdriver.PixelsArgs) error { StorageMode: storageMode, Usage: mtl.TextureUsageShaderRead | mtl.TextureUsageRenderTarget, } - t := g.view.getMTLDevice().MakeTexture(td) + t := g.view.getMTLDevice().NewTextureWithDescriptor(td) g.tmpTextures = append(g.tmpTextures, t) for _, a := range args { @@ -870,9 +870,9 @@ func (i *Image) WritePixels(args []graphicsdriver.PixelsArgs) error { } if g.cb == (mtl.CommandBuffer{}) { - g.cb = i.graphics.cq.MakeCommandBuffer() + g.cb = i.graphics.cq.CommandBuffer() } - bce := g.cb.MakeBlitCommandEncoder() + bce := g.cb.BlitCommandEncoder() for _, a := range args { so := mtl.Origin{X: a.Region.Min.X - region.Min.X, Y: a.Region.Min.Y - region.Min.Y, Z: 0} ss := mtl.Size{Width: a.Region.Dx(), Height: a.Region.Dy(), Depth: 1} @@ -914,5 +914,5 @@ func (i *Image) ensureStencil() { StorageMode: mtl.StorageModePrivate, Usage: mtl.TextureUsageRenderTarget, } - i.stencil = i.graphics.view.getMTLDevice().MakeTexture(td) + i.stencil = i.graphics.view.getMTLDevice().NewTextureWithDescriptor(td) } diff --git a/internal/graphicsdriver/metal/mtl/example_darwin_test.go b/internal/graphicsdriver/metal/mtl/example_darwin_test.go index d06522ce6..485b7b16a 100644 --- a/internal/graphicsdriver/metal/mtl/example_darwin_test.go +++ b/internal/graphicsdriver/metal/mtl/example_darwin_test.go @@ -112,15 +112,15 @@ fragment float4 FragmentShader(Vertex in [[stage_in]]) { return in.color; } ` - lib, err := device.MakeLibrary(source, mtl.CompileOptions{}) + lib, err := device.NewLibraryWithSource(source, mtl.CompileOptions{}) if err != nil { log.Fatalln(err) } - vs, err := lib.MakeFunction("VertexShader") + vs, err := lib.NewFunctionWithName("VertexShader") if err != nil { log.Fatalln(err) } - fs, err := lib.MakeFunction("FragmentShader") + fs, err := lib.NewFunctionWithName("FragmentShader") if err != nil { log.Fatalln(err) } @@ -129,7 +129,7 @@ fragment float4 FragmentShader(Vertex in [[stage_in]]) { rpld.FragmentFunction = fs rpld.ColorAttachments[0].PixelFormat = mtl.PixelFormatRGBA8UNorm rpld.ColorAttachments[0].WriteMask = mtl.ColorWriteMaskAll - rps, err := device.MakeRenderPipelineState(rpld) + rps, err := device.NewRenderPipelineStateWithDescriptor(rpld) if err != nil { log.Fatalln(err) } @@ -144,7 +144,7 @@ fragment float4 FragmentShader(Vertex in [[stage_in]]) { {f32.Vec4{-0.75, -0.75, 0, 1}, f32.Vec4{1, 1, 1, 1}}, {f32.Vec4{+0.75, -0.75, 0, 1}, f32.Vec4{0, 0, 0, 1}}, } - vertexBuffer := device.MakeBufferWithBytes(unsafe.Pointer(&vertexData[0]), unsafe.Sizeof(vertexData), mtl.ResourceStorageModeManaged) + vertexBuffer := device.NewBufferWithBytes(unsafe.Pointer(&vertexData[0]), unsafe.Sizeof(vertexData), mtl.ResourceStorageModeManaged) // Create an output texture to render into. td := mtl.TextureDescriptor{ @@ -154,10 +154,10 @@ fragment float4 FragmentShader(Vertex in [[stage_in]]) { Height: 20, StorageMode: mtl.StorageModeManaged, } - texture := device.MakeTexture(td) + texture := device.NewTextureWithDescriptor(td) - cq := device.MakeCommandQueue() - cb := cq.MakeCommandBuffer() + cq := device.NewCommandQueue() + cb := cq.CommandBuffer() // Encode all render commands. var rpd mtl.RenderPassDescriptor @@ -165,14 +165,14 @@ fragment float4 FragmentShader(Vertex in [[stage_in]]) { rpd.ColorAttachments[0].StoreAction = mtl.StoreActionStore rpd.ColorAttachments[0].ClearColor = mtl.ClearColor{Red: 0, Green: 0, Blue: 0, Alpha: 1} rpd.ColorAttachments[0].Texture = texture - rce := cb.MakeRenderCommandEncoder(rpd) + rce := cb.RenderCommandEncoderWithDescriptor(rpd) rce.SetRenderPipelineState(rps) rce.SetVertexBuffer(vertexBuffer, 0, 0) rce.DrawPrimitives(mtl.PrimitiveTypeTriangle, 0, 3) rce.EndEncoding() // Encode all blit commands. - bce := cb.MakeBlitCommandEncoder() + bce := cb.BlitCommandEncoder() bce.Synchronize(texture) bce.EndEncoding() diff --git a/internal/graphicsdriver/metal/mtl/mtl_darwin.go b/internal/graphicsdriver/metal/mtl/mtl_darwin.go index 4d2e065d2..4ee738c8c 100644 --- a/internal/graphicsdriver/metal/mtl/mtl_darwin.go +++ b/internal/graphicsdriver/metal/mtl/mtl_darwin.go @@ -36,7 +36,7 @@ import ( // GPUFamily represents the functionality for families of GPUs. // -// Reference: https://developer.apple.com/documentation/metal/mtlgpufamily +// Reference: https://developer.apple.com/documentation/metal/mtlgpufamily?language=objc. type GPUFamily int const ( @@ -54,7 +54,7 @@ const ( // FeatureSet defines a specific platform, hardware, and software configuration. // -// Reference: https://developer.apple.com/documentation/metal/mtlfeatureset. +// Reference: https://developer.apple.com/documentation/metal/mtlfeatureset?language=objc. type FeatureSet uint16 const ( @@ -92,7 +92,7 @@ const ( // TextureType defines The dimension of each image, including whether multiple images are arranged into an array or // a cube. // -// Reference: https://developer.apple.com/documentation/metal/mtltexturetype +// Reference: https://developer.apple.com/documentation/metal/mtltexturetype?language=objc. type TextureType uint16 const ( @@ -102,7 +102,7 @@ const ( // PixelFormat defines data formats that describe the organization // and characteristics of individual pixels in a texture. // -// Reference: https://developer.apple.com/documentation/metal/mtlpixelformat. +// Reference: https://developer.apple.com/documentation/metal/mtlpixelformat?language=objc. type PixelFormat uint16 // The data formats that describe the organization and characteristics @@ -117,7 +117,7 @@ const ( // PrimitiveType defines geometric primitive types for drawing commands. // -// Reference: https://developer.apple.com/documentation/metal/mtlprimitivetype. +// Reference: https://developer.apple.com/documentation/metal/mtlprimitivetype?language=objc. type PrimitiveType uint8 // Geometric primitive types for drawing commands. @@ -132,7 +132,7 @@ const ( // LoadAction defines actions performed at the start of a rendering pass // for a render command encoder. // -// Reference: https://developer.apple.com/documentation/metal/mtlloadaction. +// Reference: https://developer.apple.com/documentation/metal/mtlloadaction?language=objc. type LoadAction uint8 // Actions performed at the start of a rendering pass for a render command encoder. @@ -145,7 +145,7 @@ const ( // StoreAction defines actions performed at the end of a rendering pass // for a render command encoder. // -// Reference: https://developer.apple.com/documentation/metal/mtlstoreaction. +// Reference: https://developer.apple.com/documentation/metal/mtlstoreaction?language=objc. type StoreAction uint8 // Actions performed at the end of a rendering pass for a render command encoder. @@ -160,7 +160,7 @@ const ( // StorageMode defines the memory location and access permissions of a resource. // -// Reference: https://developer.apple.com/documentation/metal/mtlstoragemode. +// Reference: https://developer.apple.com/documentation/metal/mtlstoragemode?language=objc. type StorageMode uint8 const ( @@ -189,7 +189,7 @@ const ( // ResourceOptions defines optional arguments used to create // and influence behavior of buffer and texture objects. // -// Reference: https://developer.apple.com/documentation/metal/mtlresourceoptions. +// Reference: https://developer.apple.com/documentation/metal/mtlresourceoptions?language=objc. type ResourceOptions uint16 const ( @@ -237,7 +237,7 @@ const ( // CPUCacheMode is the CPU cache mode that defines the CPU mapping of a resource. // -// Reference: https://developer.apple.com/documentation/metal/mtlcpucachemode. +// Reference: https://developer.apple.com/documentation/metal/mtlcpucachemode?language=objc. type CPUCacheMode uint8 const ( @@ -252,7 +252,7 @@ const ( // IndexType is the index type for an index buffer that references vertices of geometric primitives. // -// Reference: https://developer.apple.com/documentation/metal/mtlstoragemode +// Reference: https://developer.apple.com/documentation/metal/mtlstoragemode?language=objc type IndexType uint8 const ( @@ -358,7 +358,7 @@ const ( // Resource represents a memory allocation for storing specialized data // that is accessible to the GPU. // -// Reference: https://developer.apple.com/documentation/metal/mtlresource. +// Reference: https://developer.apple.com/documentation/metal/mtlresource?language=objc. type Resource interface { // resource returns the underlying id pointer. resource() unsafe.Pointer @@ -366,7 +366,7 @@ type Resource interface { // RenderPipelineDescriptor configures new RenderPipelineState objects. // -// Reference: https://developer.apple.com/documentation/metal/mtlrenderpipelinedescriptor. +// Reference: https://developer.apple.com/documentation/metal/mtlrenderpipelinedescriptor?language=objc. type RenderPipelineDescriptor struct { // VertexFunction is a programmable function that processes individual vertices in a rendering pass. VertexFunction Function @@ -384,7 +384,7 @@ type RenderPipelineDescriptor struct { // RenderPipelineColorAttachmentDescriptor describes a color render target that specifies // the color configuration and color operations associated with a render pipeline. // -// Reference: https://developer.apple.com/documentation/metal/mtlrenderpipelinecolorattachmentdescriptor. +// Reference: https://developer.apple.com/documentation/metal/mtlrenderpipelinecolorattachmentdescriptor?language=objc. type RenderPipelineColorAttachmentDescriptor struct { // PixelFormat is the pixel format of the color attachment's texture. PixelFormat PixelFormat @@ -404,7 +404,7 @@ type RenderPipelineColorAttachmentDescriptor struct { // RenderPassDescriptor describes a group of render targets that serve as // the output destination for pixels generated by a render pass. // -// Reference: https://developer.apple.com/documentation/metal/mtlrenderpassdescriptor. +// Reference: https://developer.apple.com/documentation/metal/mtlrenderpassdescriptor?language=objc. type RenderPassDescriptor struct { // ColorAttachments is array of state information for attachments that store color data. ColorAttachments [1]RenderPassColorAttachmentDescriptor @@ -416,7 +416,7 @@ type RenderPassDescriptor struct { // RenderPassColorAttachmentDescriptor describes a color render target that serves // as the output destination for color pixels generated by a render pass. // -// Reference: https://developer.apple.com/documentation/metal/mtlrenderpasscolorattachmentdescriptor. +// Reference: https://developer.apple.com/documentation/metal/mtlrenderpasscolorattachmentdescriptor?language=objc. type RenderPassColorAttachmentDescriptor struct { RenderPassAttachmentDescriptor ClearColor ClearColor @@ -425,7 +425,7 @@ type RenderPassColorAttachmentDescriptor struct { // RenderPassStencilAttachment describes a stencil render target that serves as the output // destination for stencil pixels generated by a render pass. // -// Reference: https://developer.apple.com/documentation/metal/mtlrenderpassstencilattachmentdescriptor +// Reference: https://developer.apple.com/documentation/metal/mtlrenderpassstencilattachmentdescriptor?language=objc. type RenderPassStencilAttachment struct { RenderPassAttachmentDescriptor } @@ -433,7 +433,7 @@ type RenderPassStencilAttachment struct { // RenderPassAttachmentDescriptor describes a render target that serves // as the output destination for pixels generated by a render pass. // -// Reference: https://developer.apple.com/documentation/metal/mtlrenderpassattachmentdescriptor. +// Reference: https://developer.apple.com/documentation/metal/mtlrenderpassattachmentdescriptor?language=objc. type RenderPassAttachmentDescriptor struct { LoadAction LoadAction StoreAction StoreAction @@ -442,14 +442,14 @@ type RenderPassAttachmentDescriptor struct { // ClearColor is an RGBA value used for a color pixel. // -// Reference: https://developer.apple.com/documentation/metal/mtlclearcolor. +// Reference: https://developer.apple.com/documentation/metal/mtlclearcolor?language=objc. type ClearColor struct { Red, Green, Blue, Alpha float64 } // TextureDescriptor configures new Texture objects. // -// Reference: https://developer.apple.com/documentation/metal/mtltexturedescriptor. +// Reference: https://developer.apple.com/documentation/metal/mtltexturedescriptor?language=objc. type TextureDescriptor struct { TextureType TextureType PixelFormat PixelFormat @@ -462,7 +462,7 @@ type TextureDescriptor struct { // Device is abstract representation of the GPU that // serves as the primary interface for a Metal app. // -// Reference: https://developer.apple.com/documentation/metal/mtldevice. +// Reference: https://developer.apple.com/documentation/metal/mtldevice?language=objc. type Device struct { device objc.ID @@ -568,7 +568,7 @@ var ( // CreateSystemDefaultDevice returns the preferred system default Metal device. // -// Reference: https://developer.apple.com/documentation/metal/1433401-mtlcreatesystemdefaultdevice. +// Reference: https://developer.apple.com/documentation/metal/1433401-mtlcreatesystemdefaultdevice?language=objc. func CreateSystemDefaultDevice() (Device, error) { metal, err := purego.Dlopen("/System/Library/Frameworks/Metal.framework/Metal", purego.RTLD_LAZY|purego.RTLD_GLOBAL) if err != nil { @@ -608,37 +608,36 @@ func (d Device) Device() unsafe.Pointer { return *(*unsafe.Pointer)(unsafe.Point // RespondsToSelector returns a Boolean value that indicates whether the receiver implements or inherits a method that can respond to a specified message. // -// Reference: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418583-respondstoselector +// Reference: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418583-respondstoselector?language=objc. func (d Device) RespondsToSelector(sel objc.SEL) bool { return d.device.Send(sel_respondsToSelector, sel) != 0 } // SupportsFamily returns a Boolean value that indicates whether the GPU device supports the feature set of a specific GPU family. // -// Reference: https://developer.apple.com/documentation/metal/mtldevice/3143473-supportsfamily +// Reference: https://developer.apple.com/documentation/metal/mtldevice/3143473-supportsfamily?language=objc. func (d Device) SupportsFamily(gpuFamily GPUFamily) bool { return d.device.Send(sel_supportsFamily, uintptr(gpuFamily)) != 0 } // SupportsFeatureSet reports whether device d supports feature set fs. // -// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433418-supportsfeatureset. +// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433418-supportsfeatureset?language=objc. func (d Device) SupportsFeatureSet(fs FeatureSet) bool { return d.device.Send(sel_supportsFeatureSet, uintptr(fs)) != 0 } -// MakeCommandQueue creates a serial command submission queue. +// NewCommandQueue creates a queue you use to submit rendering and computation commands to a GPU. // -// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433388-makecommandqueue. -func (d Device) MakeCommandQueue() CommandQueue { +// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433388-newcommandqueue?language=objc. +func (d Device) NewCommandQueue() CommandQueue { return CommandQueue{d.device.Send(sel_newCommandQueue)} } -// MakeLibrary creates a new library that contains -// the functions stored in the specified source string. +// NewLibraryWithSource synchronously creates a Metal library instance by compiling the functions in a source string. // -// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433431-makelibrary. -func (d Device) MakeLibrary(source string, opt CompileOptions) (Library, error) { +// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433431-newlibrarywithsource?language=objc. +func (d Device) NewLibraryWithSource(source string, opt CompileOptions) (Library, error) { var err cocoa.NSError l := d.device.Send( sel_newLibraryWithSource_options_error, @@ -653,10 +652,10 @@ func (d Device) MakeLibrary(source string, opt CompileOptions) (Library, error) return Library{l}, nil } -// MakeLibraryWithData creates a Metal library instance from a data instance that contains the functions in a precompiled Metal library. +// NewLibraryWithData Creates a Metal library instance that contains the functions in a precompiled Metal library. // -// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433391-makelibrary -func (d Device) MakeLibraryWithData(buffer []byte) (Library, error) { +// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433391-newlibrarywithdata?language=objc. +func (d Device) NewLibraryWithData(buffer []byte) (Library, error) { defer runtime.KeepAlive(buffer) data := dispatchDataCreate(unsafe.Pointer(&buffer[0]), uint(len(buffer)), 0, 0) @@ -674,10 +673,10 @@ func (d Device) MakeLibraryWithData(buffer []byte) (Library, error) { return Library{l}, nil } -// MakeRenderPipelineState creates a render pipeline state object. +// NewRenderPipelineStateWithDescriptor synchronously creates a render pipeline state. // -// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433369-makerenderpipelinestate. -func (d Device) MakeRenderPipelineState(rpd RenderPipelineDescriptor) (RenderPipelineState, error) { +// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433369-newrenderpipelinestatewithdescri?language=objc. +func (d Device) NewRenderPipelineStateWithDescriptor(rpd RenderPipelineDescriptor) (RenderPipelineState, error) { renderPipelineDescriptor := objc.ID(class_MTLRenderPipelineDescriptor).Send(sel_new) renderPipelineDescriptor.Send(sel_setVertexFunction, rpd.VertexFunction.function) renderPipelineDescriptor.Send(sel_setFragmentFunction, rpd.FragmentFunction.function) @@ -705,26 +704,24 @@ func (d Device) MakeRenderPipelineState(rpd RenderPipelineDescriptor) (RenderPip return RenderPipelineState{renderPipelineState}, nil } -// MakeBufferWithBytes allocates a new buffer of a given length -// and initializes its contents by copying existing data into it. +// NewBufferWithBytes allocates a new buffer of a given length and initializes its contents by copying existing data into it. // -// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433429-makebuffer. -func (d Device) MakeBufferWithBytes(bytes unsafe.Pointer, length uintptr, opt ResourceOptions) Buffer { +// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433429-newbufferwithbytes?language=objc. +func (d Device) NewBufferWithBytes(bytes unsafe.Pointer, length uintptr, opt ResourceOptions) Buffer { return Buffer{d.device.Send(sel_newBufferWithBytes_length_options, bytes, length, uintptr(opt))} } -// MakeBufferWithLength allocates a new zero-filled buffer of a given length. +// NewBufferWithLength allocates a new zero-filled buffer of a given length. // -// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433375-newbufferwithlength -func (d Device) MakeBufferWithLength(length uintptr, opt ResourceOptions) Buffer { +// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433375-newbufferwithlength?language=objc. +func (d Device) NewBufferWithLength(length uintptr, opt ResourceOptions) Buffer { return Buffer{d.device.Send(sel_newBufferWithLength_options, length, uintptr(opt))} } -// MakeTexture creates a texture object with privately owned storage -// that contains texture state. +// NewTextureWithDescriptor creates a new texture instance. // -// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433425-maketexture. -func (d Device) MakeTexture(td TextureDescriptor) Texture { +// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433425-newtexturewithdescriptor?language=objc. +func (d Device) NewTextureWithDescriptor(td TextureDescriptor) Texture { textureDescriptor := objc.ID(class_MTLTextureDescriptor).Send(sel_new) textureDescriptor.Send(sel_setTextureType, uintptr(td.TextureType)) textureDescriptor.Send(sel_setPixelFormat, uintptr(td.PixelFormat)) @@ -739,10 +736,10 @@ func (d Device) MakeTexture(td TextureDescriptor) Texture { } } -// MakeDepthStencilState creates a new object that contains depth and stencil test state. +// NewDepthStencilStateWithDescriptor creates a depth-stencil state instance. // -// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433412-makedepthstencilstate -func (d Device) MakeDepthStencilState(dsd DepthStencilDescriptor) DepthStencilState { +// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433412-newdepthstencilstatewithdescript?language=objc. +func (d Device) NewDepthStencilStateWithDescriptor(dsd DepthStencilDescriptor) DepthStencilState { depthStencilDescriptor := objc.ID(class_MTLDepthStencilDescriptor).Send(sel_new) backFaceStencil := depthStencilDescriptor.Send(sel_backFaceStencil) backFaceStencil.Send(sel_setStencilFailureOperation, uintptr(dsd.BackFaceStencil.StencilFailureOperation)) @@ -764,14 +761,14 @@ func (d Device) MakeDepthStencilState(dsd DepthStencilDescriptor) DepthStencilSt // CompileOptions specifies optional compilation settings for // the graphics or compute functions within a library. // -// Reference: https://developer.apple.com/documentation/metal/mtlcompileoptions. +// Reference: https://developer.apple.com/documentation/metal/mtlcompileoptions?language=objc. type CompileOptions struct { // TODO. } // Drawable is a displayable resource that can be rendered or written to. // -// Reference: https://developer.apple.com/documentation/metal/mtldrawable. +// Reference: https://developer.apple.com/documentation/metal/mtldrawable?language=objc. type Drawable interface { // Drawable returns the underlying id pointer. Drawable() unsafe.Pointer @@ -780,7 +777,7 @@ type Drawable interface { // CommandQueue is a queue that organizes the order // in which command buffers are executed by the GPU. // -// Reference: https://developer.apple.com/documentation/metal/mtlcommandqueue. +// Reference: https://developer.apple.com/documentation/metal/mtlcommandqueue?language=objc. type CommandQueue struct { commandQueue objc.ID } @@ -789,17 +786,17 @@ func (cq CommandQueue) Release() { cq.commandQueue.Send(sel_release) } -// MakeCommandBuffer creates a command buffer. +// CommandBuffer returns a command buffer from the command queue that maintains strong references to resources. // -// Reference: https://developer.apple.com/documentation/metal/mtlcommandqueue/1508686-makecommandbuffer. -func (cq CommandQueue) MakeCommandBuffer() CommandBuffer { +// Reference: https://developer.apple.com/documentation/metal/mtlcommandqueue/1508686-commandbuffer?language=objc. +func (cq CommandQueue) CommandBuffer() CommandBuffer { return CommandBuffer{cq.commandQueue.Send(sel_commandBuffer)} } // CommandBuffer is a container that stores encoded commands // that are committed to and executed by the GPU. // -// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer. +// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer?language=objc. type CommandBuffer struct { commandBuffer objc.ID } @@ -814,44 +811,43 @@ func (cb CommandBuffer) Release() { // Status returns the current stage in the lifetime of the command buffer. // -// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443048-status +// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443048-status?language=objc. func (cb CommandBuffer) Status() CommandBufferStatus { return CommandBufferStatus(cb.commandBuffer.Send(sel_status)) } // PresentDrawable registers a drawable presentation to occur as soon as possible. // -// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443029-presentdrawable. +// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443029-presentdrawable?language=objc. func (cb CommandBuffer) PresentDrawable(d Drawable) { cb.commandBuffer.Send(sel_presentDrawable, d.Drawable()) } // Commit commits this command buffer for execution as soon as possible. // -// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443003-commit. +// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443003-commit?language=objc. func (cb CommandBuffer) Commit() { cb.commandBuffer.Send(sel_commit) } // WaitUntilCompleted waits for the execution of this command buffer to complete. // -// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443039-waituntilcompleted. +// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443039-waituntilcompleted?language=objc. func (cb CommandBuffer) WaitUntilCompleted() { cb.commandBuffer.Send(sel_waitUntilCompleted) } // WaitUntilScheduled blocks execution of the current thread until the command buffer is scheduled. // -// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443036-waituntilscheduled. +// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443036-waituntilscheduled?language=objc. func (cb CommandBuffer) WaitUntilScheduled() { cb.commandBuffer.Send(sel_waitUntilScheduled) } -// MakeRenderCommandEncoder creates an encoder object that can -// encode graphics rendering commands into this command buffer. +// RenderCommandEncoderWithDescriptor creates a render command encoder from a descriptor. // -// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1442999-makerendercommandencoder. -func (cb CommandBuffer) MakeRenderCommandEncoder(rpd RenderPassDescriptor) RenderCommandEncoder { +// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1442999-rendercommandencoderwithdescript?language=objc. +func (cb CommandBuffer) RenderCommandEncoderWithDescriptor(rpd RenderPassDescriptor) RenderCommandEncoder { var renderPassDescriptor = objc.ID(class_MTLRenderPassDescriptor).Send(sel_new) var colorAttachments0 = renderPassDescriptor.Send(sel_colorAttachments).Send(sel_objectAtIndexedSubscript, 0) colorAttachments0.Send(sel_setLoadAction, int(rpd.ColorAttachments[0].LoadAction)) @@ -872,11 +868,11 @@ func (cb CommandBuffer) MakeRenderCommandEncoder(rpd RenderPassDescriptor) Rende return RenderCommandEncoder{CommandEncoder{rce}} } -// MakeBlitCommandEncoder creates an encoder object that can encode +// BlitCommandEncoder creates an encoder object that can encode // memory operation (blit) commands into this command buffer. // -// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443001-makeblitcommandencoder. -func (cb CommandBuffer) MakeBlitCommandEncoder() BlitCommandEncoder { +// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443001-makeblitcommandencoder?language=objc. +func (cb CommandBuffer) BlitCommandEncoder() BlitCommandEncoder { ce := cb.commandBuffer.Send(sel_blitCommandEncoder) return BlitCommandEncoder{CommandEncoder{ce}} } @@ -884,14 +880,14 @@ func (cb CommandBuffer) MakeBlitCommandEncoder() BlitCommandEncoder { // CommandEncoder is an encoder that writes sequential GPU commands // into a command buffer. // -// Reference: https://developer.apple.com/documentation/metal/mtlcommandencoder. +// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443001-blitcommandencoder?language=objc. type CommandEncoder struct { commandEncoder objc.ID } // EndEncoding declares that all command generation from this encoder is completed. // -// Reference: https://developer.apple.com/documentation/metal/mtlcommandencoder/1458038-endencoding. +// Reference: https://developer.apple.com/documentation/metal/mtlcommandencoder/1458038-endencoding?language=objc. func (ce CommandEncoder) EndEncoding() { ce.commandEncoder.Send(sel_endEncoding) } @@ -899,7 +895,7 @@ func (ce CommandEncoder) EndEncoding() { // RenderCommandEncoder is an encoder that specifies graphics-rendering commands // and executes graphics functions. // -// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder. +// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder?language=objc. type RenderCommandEncoder struct { CommandEncoder } @@ -910,7 +906,7 @@ func (rce RenderCommandEncoder) Release() { // SetRenderPipelineState sets the current render pipeline state object. // -// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515811-setrenderpipelinestate. +// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515811-setrenderpipelinestate?language=objc. func (rce RenderCommandEncoder) SetRenderPipelineState(rps RenderPipelineState) { rce.commandEncoder.Send(sel_setRenderPipelineState, rps.renderPipelineState) } @@ -925,7 +921,7 @@ func (rce RenderCommandEncoder) SetViewport(viewport Viewport) { // SetScissorRect sets the scissor rectangle for a fragment scissor test. // -// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515583-setscissorrect +// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515583-setscissorrect?language=objc. func (rce RenderCommandEncoder) SetScissorRect(scissorRect ScissorRect) { inv := cocoa.NSInvocation_invocationWithMethodSignature(cocoa.NSMethodSignature_signatureWithObjCTypes("v@:{MTLScissorRect=qqqq}")) inv.SetTarget(rce.commandEncoder) @@ -937,14 +933,14 @@ func (rce RenderCommandEncoder) SetScissorRect(scissorRect ScissorRect) { // SetVertexBuffer sets a buffer for the vertex shader function at an index // in the buffer argument table with an offset that specifies the start of the data. // -// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515829-setvertexbuffer. +// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515829-setvertexbuffer?language=objc. func (rce RenderCommandEncoder) SetVertexBuffer(buf Buffer, offset, index int) { rce.commandEncoder.Send(sel_setVertexBuffer_offset_atIndex, buf.buffer, offset, index) } // SetVertexBytes sets a block of data for the vertex function. // -// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515846-setvertexbytes. +// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515846-setvertexbytes?language=objc. func (rce RenderCommandEncoder) SetVertexBytes(bytes unsafe.Pointer, length uintptr, index int) { rce.commandEncoder.Send(sel_setVertexBytes_length_atIndex, bytes, length, index) } @@ -955,7 +951,7 @@ func (rce RenderCommandEncoder) SetFragmentBytes(bytes unsafe.Pointer, length ui // SetFragmentTexture sets a texture for the fragment function at an index in the texture argument table. // -// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515390-setfragmenttexture +// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515390-setfragmenttexture?language=objc. func (rce RenderCommandEncoder) SetFragmentTexture(texture Texture, index int) { rce.commandEncoder.Send(sel_setFragmentTexture_atIndex, texture.texture, index) } @@ -966,7 +962,7 @@ func (rce RenderCommandEncoder) SetBlendColor(red, green, blue, alpha float32) { // SetDepthStencilState sets the depth and stencil test state. // -// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1516119-setdepthstencilstate +// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1516119-setdepthstencilstate?language=objc. func (rce RenderCommandEncoder) SetDepthStencilState(depthStencilState DepthStencilState) { rce.commandEncoder.Send(sel_setDepthStencilState, depthStencilState.depthStencilState) } @@ -974,7 +970,7 @@ func (rce RenderCommandEncoder) SetDepthStencilState(depthStencilState DepthSten // DrawPrimitives renders one instance of primitives using vertex data // in contiguous array elements. // -// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1516326-drawprimitives. +// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1516326-drawprimitives?language=objc. func (rce RenderCommandEncoder) DrawPrimitives(typ PrimitiveType, vertexStart, vertexCount int) { rce.commandEncoder.Send(sel_drawPrimitives_vertexStart_vertexCount, uintptr(typ), vertexStart, vertexCount) } @@ -991,7 +987,7 @@ func (rce RenderCommandEncoder) DrawIndexedPrimitives(typ PrimitiveType, indexCo // BlitCommandEncoder is an encoder that specifies resource copy // and resource synchronization commands. // -// Reference: https://developer.apple.com/documentation/metal/mtlblitcommandencoder. +// Reference: https://developer.apple.com/documentation/metal/mtlblitcommandencoder?language=objc. type BlitCommandEncoder struct { CommandEncoder } @@ -999,7 +995,7 @@ type BlitCommandEncoder struct { // Synchronize flushes any copy of the specified resource from its corresponding // Device caches and, if needed, invalidates any CPU caches. // -// Reference: https://developer.apple.com/documentation/metal/mtlblitcommandencoder/1400775-synchronize. +// Reference: https://developer.apple.com/documentation/metal/mtlblitcommandencoder/1400775-synchronize?language=objc. func (bce BlitCommandEncoder) Synchronize(resource Resource) { if runtime.GOOS == "ios" { return @@ -1032,15 +1028,15 @@ func (bce BlitCommandEncoder) CopyFromTexture(sourceTexture Texture, sourceSlice // Library is a collection of compiled graphics or compute functions. // -// Reference: https://developer.apple.com/documentation/metal/mtllibrary. +// Reference: https://developer.apple.com/documentation/metal/mtllibrary?language=objc. type Library struct { library objc.ID } -// MakeFunction returns a pre-compiled, non-specialized function. +// NewFunctionWithName returns a pre-compiled, non-specialized function. // -// Reference: https://developer.apple.com/documentation/metal/mtllibrary/1515524-makefunction. -func (l Library) MakeFunction(name string) (Function, error) { +// Reference: https://developer.apple.com/documentation/metal/mtllibrary/1515524-newfunctionwithname?language=objc. +func (l Library) NewFunctionWithName(name string) (Function, error) { f := l.library.Send(sel_newFunctionWithName, cocoa.NSString_alloc().InitWithUTF8String(name).ID, ) @@ -1057,7 +1053,7 @@ func (l Library) Release() { // Texture is a memory allocation for storing formatted // image data that is accessible to the GPU. // -// Reference: https://developer.apple.com/documentation/metal/mtltexture. +// Reference: https://developer.apple.com/documentation/metal/mtltexture?language=objc. type Texture struct { texture objc.ID } @@ -1077,7 +1073,7 @@ func (t Texture) Release() { // GetBytes copies a block of pixels from the storage allocation of texture // slice zero into system memory at a specified address. // -// Reference: https://developer.apple.com/documentation/metal/mtltexture/1515751-getbytes. +// Reference: https://developer.apple.com/documentation/metal/mtltexture/1515751-getbytes?language=objc. func (t Texture) GetBytes(pixelBytes *byte, bytesPerRow uintptr, region Region, level int) { inv := cocoa.NSInvocation_invocationWithMethodSignature(cocoa.NSMethodSignature_signatureWithObjCTypes("v@:^vQ{MTLRegion={MTLOrigin=qqq}{MTLSize=qqq}}Q")) inv.SetTarget(t.texture) @@ -1091,7 +1087,7 @@ func (t Texture) GetBytes(pixelBytes *byte, bytesPerRow uintptr, region Region, // ReplaceRegion copies a block of pixels from the caller's pointer into the storage allocation for slice 0 of a texture. // -// Reference: https://developer.apple.com/documentation/metal/mtltexture/1515464-replaceregion +// Reference: https://developer.apple.com/documentation/metal/mtltexture/1515464-replaceregion?language=objc. func (t Texture) ReplaceRegion(region Region, level int, pixelBytes unsafe.Pointer, bytesPerRow int) { inv := cocoa.NSInvocation_invocationWithMethodSignature(cocoa.NSMethodSignature_signatureWithObjCTypes("v@:{MTLRegion={MTLOrigin=qqq}{MTLSize=qqq}}Q^vQ")) inv.SetTarget(t.texture) @@ -1105,14 +1101,14 @@ func (t Texture) ReplaceRegion(region Region, level int, pixelBytes unsafe.Point // Width is the width of the texture image for the base level mipmap, in pixels. // -// Reference: https://developer.apple.com/documentation/metal/mtltexture/1515339-width +// Reference: https://developer.apple.com/documentation/metal/mtltexture/1515339-width?language=objc. func (t Texture) Width() int { return int(t.texture.Send(sel_width)) } // Height is the height of the texture image for the base level mipmap, in pixels. // -// Reference: https://developer.apple.com/documentation/metal/mtltexture/1515938-height +// Reference: https://developer.apple.com/documentation/metal/mtltexture/1515938-height?language=objc. func (t Texture) Height() int { return int(t.texture.Send(sel_height)) } @@ -1120,7 +1116,7 @@ func (t Texture) Height() int { // Buffer is a memory allocation for storing unformatted data // that is accessible to the GPU. // -// Reference: https://developer.apple.com/documentation/metal/mtlbuffer. +// Reference: https://developer.apple.com/documentation/metal/mtlbuffer?language=objc. type Buffer struct { buffer objc.ID } @@ -1153,7 +1149,7 @@ func (b Buffer) Native() unsafe.Pointer { // Function represents a programmable graphics or compute function executed by the GPU. // -// Reference: https://developer.apple.com/documentation/metal/mtlfunction. +// Reference: https://developer.apple.com/documentation/metal/mtlfunction?language=objc. type Function struct { function objc.ID } @@ -1165,7 +1161,7 @@ func (f Function) Release() { // RenderPipelineState contains the graphics functions // and configuration state used in a render pass. // -// Reference: https://developer.apple.com/documentation/metal/mtlrenderpipelinestate. +// Reference: https://developer.apple.com/documentation/metal/mtlrenderpipelinestate?language=objc. type RenderPipelineState struct { renderPipelineState objc.ID } @@ -1177,7 +1173,7 @@ func (r RenderPipelineState) Release() { // Region is a rectangular block of pixels in an image or texture, // defined by its upper-left corner and its size. // -// Reference: https://developer.apple.com/documentation/metal/mtlregion. +// Reference: https://developer.apple.com/documentation/metal/mtlregion?language=objc. type Region struct { Origin Origin // The location of the upper-left corner of the block. Size Size // The size of the block. @@ -1186,18 +1182,18 @@ type Region struct { // Origin represents the location of a pixel in an image or texture relative // to the upper-left corner, whose coordinates are (0, 0). // -// Reference: https://developer.apple.com/documentation/metal/mtlorigin. +// Reference: https://developer.apple.com/documentation/metal/mtlorigin?language=objc. type Origin struct{ X, Y, Z int } // Size represents the set of dimensions that declare the size of an object, // such as an image, texture, threadgroup, or grid. // -// Reference: https://developer.apple.com/documentation/metal/mtlsize. +// Reference: https://developer.apple.com/documentation/metal/mtlsize?language=objc. type Size struct{ Width, Height, Depth int } // RegionMake2D returns a 2D, rectangular region for image or texture data. // -// Reference: https://developer.apple.com/documentation/metal/1515675-mtlregionmake2d. +// Reference: https://developer.apple.com/documentation/metal/1515675-mtlregionmake2d?language=objc. func RegionMake2D(x, y, width, height int) Region { return Region{ Origin: Origin{x, y, 0}, @@ -1216,7 +1212,7 @@ type Viewport struct { // ScissorRect represents a rectangle for the scissor fragment test. // -// Reference: https://developer.apple.com/documentation/metal/mtlscissorrect +// Reference: https://developer.apple.com/documentation/metal/mtlscissorrect?language=objc. type ScissorRect struct { X int Y int @@ -1226,7 +1222,7 @@ type ScissorRect struct { // DepthStencilState is a depth and stencil state object that specifies the depth and stencil configuration and operations used in a render pass. // -// Reference: https://developer.apple.com/documentation/metal/mtldepthstencilstate +// Reference: https://developer.apple.com/documentation/metal/mtldepthstencilstate?language=objc. type DepthStencilState struct { depthStencilState objc.ID } @@ -1237,7 +1233,7 @@ func (d DepthStencilState) Release() { // DepthStencilDescriptor is an object that configures new MTLDepthStencilState objects. // -// Reference: https://developer.apple.com/documentation/metal/mtldepthstencildescriptor +// Reference: https://developer.apple.com/documentation/metal/mtldepthstencildescriptor?language=objc. type DepthStencilDescriptor struct { // BackFaceStencil is the stencil descriptor for back-facing primitives. BackFaceStencil StencilDescriptor @@ -1248,7 +1244,7 @@ type DepthStencilDescriptor struct { // StencilDescriptor is an object that defines the front-facing or back-facing stencil operations of a depth and stencil state object. // -// Reference: https://developer.apple.com/documentation/metal/mtlstencildescriptor +// Reference: https://developer.apple.com/documentation/metal/mtlstencildescriptor?language=objc. type StencilDescriptor struct { // StencilFailureOperation is the operation that is performed to update the values in the stencil attachment when the stencil test fails. StencilFailureOperation StencilOperation diff --git a/internal/graphicsdriver/metal/mtl/mtl_darwin_test.go b/internal/graphicsdriver/metal/mtl/mtl_darwin_test.go index 4abc65c09..39e2c1bfb 100644 --- a/internal/graphicsdriver/metal/mtl/mtl_darwin_test.go +++ b/internal/graphicsdriver/metal/mtl/mtl_darwin_test.go @@ -56,15 +56,15 @@ fragment float4 FragmentShader(Vertex in [[stage_in]]) { return in.color; } ` - lib, err := device.MakeLibrary(source, mtl.CompileOptions{}) + lib, err := device.NewLibraryWithSource(source, mtl.CompileOptions{}) if err != nil { t.Fatal(err) } - vs, err := lib.MakeFunction("VertexShader") + vs, err := lib.NewFunctionWithName("VertexShader") if err != nil { t.Fatal(err) } - fs, err := lib.MakeFunction("FragmentShader") + fs, err := lib.NewFunctionWithName("FragmentShader") if err != nil { t.Fatal(err) } @@ -72,7 +72,7 @@ fragment float4 FragmentShader(Vertex in [[stage_in]]) { rpld.VertexFunction = vs rpld.FragmentFunction = fs rpld.ColorAttachments[0].PixelFormat = mtl.PixelFormatRGBA8UNorm - rps, err := device.MakeRenderPipelineState(rpld) + rps, err := device.NewRenderPipelineStateWithDescriptor(rpld) if err != nil { t.Fatal(err) } @@ -87,7 +87,7 @@ fragment float4 FragmentShader(Vertex in [[stage_in]]) { {f32.Vec4{-0.75, -0.75, 0, 1}, f32.Vec4{0, 1, 0, 1}}, {f32.Vec4{+0.75, -0.75, 0, 1}, f32.Vec4{0, 0, 1, 1}}, } - vertexBuffer := device.MakeBufferWithBytes(unsafe.Pointer(&vertexData[0]), unsafe.Sizeof(vertexData), mtl.ResourceStorageModeManaged) + vertexBuffer := device.NewBufferWithBytes(unsafe.Pointer(&vertexData[0]), unsafe.Sizeof(vertexData), mtl.ResourceStorageModeManaged) // Create an output texture to render into. td := mtl.TextureDescriptor{ @@ -97,10 +97,10 @@ fragment float4 FragmentShader(Vertex in [[stage_in]]) { Height: 512, StorageMode: mtl.StorageModeManaged, } - texture := device.MakeTexture(td) + texture := device.NewTextureWithDescriptor(td) - cq := device.MakeCommandQueue() - cb := cq.MakeCommandBuffer() + cq := device.NewCommandQueue() + cb := cq.CommandBuffer() // Encode all render commands. var rpd mtl.RenderPassDescriptor @@ -108,14 +108,14 @@ fragment float4 FragmentShader(Vertex in [[stage_in]]) { rpd.ColorAttachments[0].StoreAction = mtl.StoreActionStore rpd.ColorAttachments[0].ClearColor = mtl.ClearColor{Red: 0.35, Green: 0.65, Blue: 0.85, Alpha: 1} rpd.ColorAttachments[0].Texture = texture - rce := cb.MakeRenderCommandEncoder(rpd) + rce := cb.RenderCommandEncoderWithDescriptor(rpd) rce.SetRenderPipelineState(rps) rce.SetVertexBuffer(vertexBuffer, 0, 0) rce.DrawPrimitives(mtl.PrimitiveTypeTriangle, 0, 3) rce.EndEncoding() // Encode all blit commands. - bce := cb.MakeBlitCommandEncoder() + bce := cb.BlitCommandEncoder() bce.Synchronize(texture) bce.EndEncoding() diff --git a/internal/graphicsdriver/metal/shader_darwin.go b/internal/graphicsdriver/metal/shader_darwin.go index 87823978b..2ec9c9934 100644 --- a/internal/graphicsdriver/metal/shader_darwin.go +++ b/internal/graphicsdriver/metal/shader_darwin.go @@ -104,28 +104,28 @@ func (s *Shader) Dispose() { func (s *Shader) init(device mtl.Device) error { var src string if libBin := thePrecompiledLibraries.get(s.ir.SourceHash); len(libBin) > 0 { - lib, err := device.MakeLibraryWithData(libBin) + lib, err := device.NewLibraryWithData(libBin) if err != nil { return err } s.lib = lib } else { src = msl.Compile(s.ir) - lib, err := device.MakeLibrary(src, mtl.CompileOptions{}) + lib, err := device.NewLibraryWithSource(src, mtl.CompileOptions{}) if err != nil { return fmt.Errorf("metal: device.MakeLibrary failed: %w, source: %s", err, src) } s.lib = lib } - vs, err := s.lib.MakeFunction(msl.VertexName) + vs, err := s.lib.NewFunctionWithName(msl.VertexName) if err != nil { if src != "" { return fmt.Errorf("metal: lib.MakeFunction for vertex failed: %w, source: %s", err, src) } return fmt.Errorf("metal: lib.MakeFunction for vertex failed: %w", err) } - fs, err := s.lib.MakeFunction(msl.FragmentName) + fs, err := s.lib.NewFunctionWithName(msl.FragmentName) if err != nil { if src != "" { return fmt.Errorf("metal: lib.MakeFunction for fragment failed: %w, source: %s", err, src) @@ -176,7 +176,7 @@ func (s *Shader) RenderPipelineState(view *view, blend graphicsdriver.Blend, ste rpld.ColorAttachments[0].WriteMask = mtl.ColorWriteMaskNone } - rps, err := view.getMTLDevice().MakeRenderPipelineState(rpld) + rps, err := view.getMTLDevice().NewRenderPipelineStateWithDescriptor(rpld) if err != nil { return mtl.RenderPipelineState{}, err } From b3ad97b22b4ab53c501ed3832e80281cefc88011 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 6 May 2024 21:58:44 +0900 Subject: [PATCH 46/81] internal/graphicsdriver/metal/mtl: add comments --- .../graphicsdriver/metal/mtl/mtl_darwin.go | 43 ++++++++++++++----- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/internal/graphicsdriver/metal/mtl/mtl_darwin.go b/internal/graphicsdriver/metal/mtl/mtl_darwin.go index 4ee738c8c..85da17128 100644 --- a/internal/graphicsdriver/metal/mtl/mtl_darwin.go +++ b/internal/graphicsdriver/metal/mtl/mtl_darwin.go @@ -1003,6 +1003,9 @@ func (bce BlitCommandEncoder) Synchronize(resource Resource) { bce.commandEncoder.Send(sel_synchronizeResource, resource.resource()) } +// SynchronizeTexture encodes a command that synchronizes a part of the CPU’s copy of a texture so that it matches the GPU’s copy. +// +// Reference: https://developer.apple.com/documentation/metal/mtlblitcommandencoder/1400757-synchronizetexture?language=objc. func (bce BlitCommandEncoder) SynchronizeTexture(texture Texture, slice int, level int) { if runtime.GOOS == "ios" { return @@ -1010,6 +1013,9 @@ func (bce BlitCommandEncoder) SynchronizeTexture(texture Texture, slice int, lev bce.commandEncoder.Send(sel_synchronizeTexture_slice_level, texture.texture, slice, level) } +// CopyFromTexture encodes a command that copies image data from a texture’s slice into another slice. +// +// Reference: https://developer.apple.com/documentation/metal/mtlblitcommandencoder/1400754-copyfromtexture?language=objc. func (bce BlitCommandEncoder) CopyFromTexture(sourceTexture Texture, sourceSlice int, sourceLevel int, sourceOrigin Origin, sourceSize Size, destinationTexture Texture, destinationSlice int, destinationLevel int, destinationOrigin Origin) { inv := cocoa.NSInvocation_invocationWithMethodSignature(cocoa.NSMethodSignature_signatureWithObjCTypes("v@:@QQ{MTLOrigin=qqq}{MTLSize=qqq}@QQ{MTLOrigin=qqq}")) inv.SetTarget(bce.commandEncoder) @@ -1064,7 +1070,9 @@ func NewTexture(texture objc.ID) Texture { } // resource implements the Resource interface. -func (t Texture) resource() unsafe.Pointer { return *(*unsafe.Pointer)(unsafe.Pointer(&t.texture)) } +func (t Texture) resource() unsafe.Pointer { + return *(*unsafe.Pointer)(unsafe.Pointer(&t.texture)) +} func (t Texture) Release() { t.texture.Send(sel_release) @@ -1121,8 +1129,14 @@ type Buffer struct { buffer objc.ID } -func (b Buffer) resource() unsafe.Pointer { return *(*unsafe.Pointer)(unsafe.Pointer(&b.buffer)) } +// resource implements the Resource interface. +func (b Buffer) resource() unsafe.Pointer { + return *(*unsafe.Pointer)(unsafe.Pointer(&b.buffer)) +} +// Length returns the logical size of the buffer, in bytes. +// +// Reference: https://developer.apple.com/documentation/metal/mtlbuffer/1515373-length?language=objc. func (b Buffer) Length() uintptr { return uintptr(b.buffer.Send(sel_length)) } @@ -1143,10 +1157,6 @@ func (b Buffer) Release() { b.buffer.Send(sel_release) } -func (b Buffer) Native() unsafe.Pointer { - return *(*unsafe.Pointer)(unsafe.Pointer(&b.buffer)) -} - // Function represents a programmable graphics or compute function executed by the GPU. // // Reference: https://developer.apple.com/documentation/metal/mtlfunction?language=objc. @@ -1183,24 +1193,35 @@ type Region struct { // to the upper-left corner, whose coordinates are (0, 0). // // Reference: https://developer.apple.com/documentation/metal/mtlorigin?language=objc. -type Origin struct{ X, Y, Z int } +type Origin struct { + X int + Y int + Z int +} // Size represents the set of dimensions that declare the size of an object, // such as an image, texture, threadgroup, or grid. // // Reference: https://developer.apple.com/documentation/metal/mtlsize?language=objc. -type Size struct{ Width, Height, Depth int } +type Size struct { + Width int + Height int + Depth int +} // RegionMake2D returns a 2D, rectangular region for image or texture data. // // Reference: https://developer.apple.com/documentation/metal/1515675-mtlregionmake2d?language=objc. func RegionMake2D(x, y, width, height int) Region { return Region{ - Origin: Origin{x, y, 0}, - Size: Size{width, height, 1}, + Origin: Origin{X: x, Y: y, Z: 0}, + Size: Size{Width: width, Height: height, Depth: 1}, } } +// Viewport is a 3D rectangular region for the viewport clipping. +// +// Reference: https://developer.apple.com/documentation/metal/mtlviewport?language=objc. type Viewport struct { OriginX float64 OriginY float64 @@ -1210,7 +1231,7 @@ type Viewport struct { ZFar float64 } -// ScissorRect represents a rectangle for the scissor fragment test. +// ScissorRect is a rectangle for the scissor fragment test. // // Reference: https://developer.apple.com/documentation/metal/mtlscissorrect?language=objc. type ScissorRect struct { From 5926b37c98f18bd1f510174495aefc5520bfe391 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 6 May 2024 22:11:24 +0900 Subject: [PATCH 47/81] all: update golang.org/x/sys to v0.20.0 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0695b54aa..3450bf170 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/kisielk/errcheck v1.7.0 golang.org/x/image v0.15.0 golang.org/x/sync v0.7.0 - golang.org/x/sys v0.19.0 + golang.org/x/sys v0.20.0 golang.org/x/text v0.14.0 golang.org/x/tools v0.20.0 ) diff --git a/go.sum b/go.sum index adc16194f..325b1e6fb 100644 --- a/go.sum +++ b/go.sum @@ -53,8 +53,8 @@ golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= From 724ce25260fbab10b8453f61a878101b8f7d54b6 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Thu, 9 May 2024 03:32:22 +0900 Subject: [PATCH 48/81] all: update dependencies --- go.mod | 6 +++--- go.sum | 10 ++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 3450bf170..d8673ec89 100644 --- a/go.mod +++ b/go.mod @@ -15,11 +15,11 @@ require ( github.com/jezek/xgb v1.1.1 github.com/jfreymuth/oggvorbis v1.0.5 github.com/kisielk/errcheck v1.7.0 - golang.org/x/image v0.15.0 + golang.org/x/image v0.16.0 golang.org/x/sync v0.7.0 golang.org/x/sys v0.20.0 - golang.org/x/text v0.14.0 - golang.org/x/tools v0.20.0 + golang.org/x/text v0.15.0 + golang.org/x/tools v0.21.0 ) require ( diff --git a/go.sum b/go.sum index 325b1e6fb..85f5acfe7 100644 --- a/go.sum +++ b/go.sum @@ -30,8 +30,9 @@ github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjL github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8= golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= +golang.org/x/image v0.16.0 h1:9kloLAKhUufZhA12l5fwnx2NZW39/we1UhBesW433jw= +golang.org/x/image v0.16.0/go.mod h1:ugSZItdV4nOxyqp56HmXwH0Ry0nBCpjnZdpDaIHdoPs= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= @@ -62,12 +63,13 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= -golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= +golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= +golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From db454548be1ae79e674ab95b89dc675181277e21 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Thu, 9 May 2024 12:09:47 +0900 Subject: [PATCH 49/81] all: update README --- README.md | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/README.md b/README.md index a87e26423..5e8ecbd9d 100644 --- a/README.md +++ b/README.md @@ -61,3 +61,132 @@ For installation on desktops, see [the installation instruction](https://ebiteng Ebitengine is licensed under Apache license version 2.0. See [LICENSE](LICENSE) file. [The Ebitengine logo](https://ebitengine.org/images/logo.png) by Hajime Hoshi is licensed under [the Creative Commons Attribution-NoDerivatives 4.0](https://creativecommons.org/licenses/by-nd/4.0/). + +### GLFW + +https://github.com/glfw/glfw + + +``` +Copyright (c) 2002-2006 Marcus Geelnard + +Copyright (c) 2006-2019 Camilla Löwy + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. +``` + +### Go + +https://cs.opensource.google/go/go + + +``` +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +``` + +### go-gl/gl + +https://github.com/go-gl/gl + + +``` +The MIT License (MIT) + +Copyright (c) 2014 Eric Woroshow + +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. +``` + +### go-gl/glfw + +https://github.com/go-gl/glfw + + +``` +Copyright (c) 2012 The glfw3-go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +``` From bfa94359062cfd769f334a86016af103d8ae67ee Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Fri, 10 May 2024 18:24:47 +0900 Subject: [PATCH 50/81] ebiten: add comments about Layout and LayoutF Updates #2988 --- run.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/run.go b/run.go index 683795da8..2291562e5 100644 --- a/run.go +++ b/run.go @@ -64,7 +64,8 @@ type Game interface { Draw(screen *Image) // Layout accepts a native outside size in device-independent pixels and returns the game's logical screen - // size. + // size in pixels. The logical size is used for 1) the screen size given at Draw and 2) calculation of the + // scale from the screen to the final screen size. // // On desktops, the outside is a window or a monitor (fullscreen mode). On browsers, the outside is a body // element. On mobiles, the outside is the view's size. @@ -90,6 +91,11 @@ type LayoutFer interface { // LayoutF is the float version of Game.Layout. // // If the game implements this interface, Layout is never called and LayoutF is called instead. + // + // LayoutF accepts a native outside size in device-independent pixels and returns the game's logical screen + // size in pixels. The logical size is used for 1) the screen size given at Draw and 2) calculation of the + // scale from the screen to the final screen size. For 1), the actual screen size is a rounded up of the + // logical size. LayoutF(outsideWidth, outsideHeight float64) (screenWidth, screenHeight float64) } From a108dac797e9248fc93d2b9616af9e453725c8bb Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 11 May 2024 18:58:59 +0900 Subject: [PATCH 51/81] internal/shaderir: add 'pssl' package --- internal/shaderir/pssl/pssl.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 internal/shaderir/pssl/pssl.go diff --git a/internal/shaderir/pssl/pssl.go b/internal/shaderir/pssl/pssl.go new file mode 100644 index 000000000..5bc8c8e20 --- /dev/null +++ b/internal/shaderir/pssl/pssl.go @@ -0,0 +1,17 @@ +// Copyright 2024 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// The actual implementation will be provided by -overlay. + +package pssl From 5bb060b1e9a90c90c52ba79a517f285068e355be Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 12 May 2024 14:19:02 +0900 Subject: [PATCH 52/81] shaderprecomp: add pssl --- .../playstation5/graphics_playstation5.go | 4 ++ internal/shaderir/pssl/pssl.go | 8 +++ shaderprecomp/shaderprecomp_darwin.go | 2 + shaderprecomp/shaderprecomp_playstation5.go | 51 +++++++++++++++++++ shaderprecomp/shaderprecomp_windows.go | 2 + 5 files changed, 67 insertions(+) create mode 100644 shaderprecomp/shaderprecomp_playstation5.go diff --git a/internal/graphicsdriver/playstation5/graphics_playstation5.go b/internal/graphicsdriver/playstation5/graphics_playstation5.go index a9bc03279..7ac2a3d3e 100644 --- a/internal/graphicsdriver/playstation5/graphics_playstation5.go +++ b/internal/graphicsdriver/playstation5/graphics_playstation5.go @@ -153,3 +153,7 @@ func (s *Shader) ID() graphicsdriver.ShaderID { func (s *Shader) Dispose() { C.ebitengine_DisposeShader(C.int(s.id)) } + +func RegisterPrecompiledShaders(hash shaderir.SourceHash, vertex, pixel []byte) { + // TODO: Implement this. +} diff --git a/internal/shaderir/pssl/pssl.go b/internal/shaderir/pssl/pssl.go index 5bc8c8e20..2f5c4ba1e 100644 --- a/internal/shaderir/pssl/pssl.go +++ b/internal/shaderir/pssl/pssl.go @@ -15,3 +15,11 @@ // The actual implementation will be provided by -overlay. package pssl + +import ( + "github.com/hajimehoshi/ebiten/v2/internal/shaderir" +) + +func Compile(p *shaderir.Program) (vertexShader, pixelShader string) { + panic("not implemented") +} diff --git a/shaderprecomp/shaderprecomp_darwin.go b/shaderprecomp/shaderprecomp_darwin.go index ed2eaa9ce..b795ba685 100644 --- a/shaderprecomp/shaderprecomp_darwin.go +++ b/shaderprecomp/shaderprecomp_darwin.go @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build !playstation5 + package shaderprecomp import ( diff --git a/shaderprecomp/shaderprecomp_playstation5.go b/shaderprecomp/shaderprecomp_playstation5.go new file mode 100644 index 000000000..102ed5b0c --- /dev/null +++ b/shaderprecomp/shaderprecomp_playstation5.go @@ -0,0 +1,51 @@ +// Copyright 2024 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build playstation5 + +package shaderprecomp + +import ( + "io" + + "github.com/hajimehoshi/ebiten/v2/internal/graphics" + "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/playstation5" + "github.com/hajimehoshi/ebiten/v2/internal/shaderir" + "github.com/hajimehoshi/ebiten/v2/internal/shaderir/pssl" +) + +// CompileToPSSL compiles the shader source to PlayStaton Shader Language to writers. +// +// CompileToPSSL is concurrent-safe. +func CompileToPSSL(vertexWriter, pixelWriter io.Writer, source *ShaderSource) error { + ir, err := graphics.CompileShader(source.source) + if err != nil { + return err + } + vs, ps := pssl.Compile(ir) + if _, err = vertexWriter.Write([]byte(vs)); err != nil { + return err + } + if _, err = pixelWriter.Write([]byte(ps)); err != nil { + return err + } + return nil +} + +// RegisterPlayStationShaders registers a precompiled PlayStation Shader for a shader source. +// +// RegisterPlayStationShaders is concurrent-safe. +func RegisterPlayStationShaders(source *ShaderSource, vertexShader, pixelShader []byte) { + playstation5.RegisterPrecompiledShaders(shaderir.SourceHash(source.ID()), vertexShader, pixelShader) +} diff --git a/shaderprecomp/shaderprecomp_windows.go b/shaderprecomp/shaderprecomp_windows.go index f1b96bdbb..27c9de404 100644 --- a/shaderprecomp/shaderprecomp_windows.go +++ b/shaderprecomp/shaderprecomp_windows.go @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build !playstation5 + package shaderprecomp import ( From 2c2a1fe859b0bdb86c1487585622ff64064bc863 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 18 May 2024 16:53:29 +0900 Subject: [PATCH 53/81] all: update gomobile This fixes the issue with Android SDK 34. Closes #2992 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d8673ec89..87af5abab 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/hajimehoshi/ebiten/v2 go 1.19 require ( - github.com/ebitengine/gomobile v0.0.0-20240429094902-cf88669c3591 + github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895 github.com/ebitengine/hideconsole v1.0.0 github.com/ebitengine/oto/v3 v3.3.0-alpha.1 github.com/ebitengine/purego v0.8.0-alpha.2 diff --git a/go.sum b/go.sum index 85f5acfe7..eeec488de 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/ebitengine/gomobile v0.0.0-20240429094902-cf88669c3591 h1:iCauZ8Q3KW2EHpj+Re4qRU4/Y7ustOTpNV6m8hx5BqE= -github.com/ebitengine/gomobile v0.0.0-20240429094902-cf88669c3591/go.mod h1:7zsHIJi2c+Jo+wIL9wD+ADWzMbtG8jhUQpplXIrZ7EI= +github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895 h1:48bCqKTuD7Z0UovDfvpCn7wZ0GUZ+yosIteNDthn3FU= +github.com/ebitengine/gomobile v0.0.0-20240518074828-e86332849895/go.mod h1:XZdLv05c5hOZm3fM2NlJ92FyEZjnslcMcNRrhxs8+8M= github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= github.com/ebitengine/oto/v3 v3.3.0-alpha.1 h1:J2nBmQwPLKc4+yLObytq1jKNydI96l6EjZfgefiqGbk= From 53de367d47019452cb4a0a35a699e95d5c7693a2 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 18 May 2024 17:06:48 +0900 Subject: [PATCH 54/81] all: update typesetting to v0.1.1 --- go.mod | 2 +- go.sum | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 87af5abab..ccedbace8 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/ebitengine/oto/v3 v3.3.0-alpha.1 github.com/ebitengine/purego v0.8.0-alpha.2 github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f - github.com/go-text/typesetting v0.1.1-0.20240402181327-ced1d6822703 + github.com/go-text/typesetting v0.1.1 github.com/hajimehoshi/bitmapfont/v3 v3.1.0 github.com/hajimehoshi/go-mp3 v0.3.4 github.com/jakecoffman/cp v1.2.1 diff --git a/go.sum b/go.sum index eeec488de..2805f8e14 100644 --- a/go.sum +++ b/go.sum @@ -8,9 +8,9 @@ github.com/ebitengine/purego v0.8.0-alpha.2 h1:+Kyr9n4eXAGMzhtWJxfdQ7AzGn0+6ZWih github.com/ebitengine/purego v0.8.0-alpha.2/go.mod h1:w5fARo4H5UrAgQTz0yqDfZ6bjstTQwUFmO+TN+nHlWE= github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f h1:ysqRe+lvUiL0dH5XzkH0Bz68bFMPJ4f5Si4L/HD9SGk= github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f/go.mod h1:i/ebyRRv/IoHixuZ9bElZnXbmfoUVPGQpdsJ4sVuX38= -github.com/go-text/typesetting v0.1.1-0.20240402181327-ced1d6822703 h1:AqtMl9yw7r319Ah4W2afQm3Ql+PEsQKHds18tGvKhog= -github.com/go-text/typesetting v0.1.1-0.20240402181327-ced1d6822703/go.mod h1:2+owI/sxa73XA581LAzVuEBZ3WEEV2pXeDswCH/3i1I= -github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66 h1:GUrm65PQPlhFSKjLPGOZNPNxLCybjzjYBzjfoBGaDUY= +github.com/go-text/typesetting v0.1.1 h1:bGAesCuo85nXnEN5LmFMVGAGpGkCPtHrZLi//qD7EJo= +github.com/go-text/typesetting v0.1.1/go.mod h1:d22AnmeKq/on0HNv73UFriMKc4Ez6EqZAofLhAzpSzI= +github.com/go-text/typesetting-utils v0.0.0-20231211103740-d9332ae51f04 h1:zBx+p/W2aQYtNuyZNcTfinWvXBQwYtDfme051PR/lAY= github.com/hajimehoshi/bitmapfont/v3 v3.1.0 h1:JLy/na2e83GewqebpFbS2LHpDVnGdzmyJOpqXtBgLm0= github.com/hajimehoshi/bitmapfont/v3 v3.1.0/go.mod h1:VVaVK/4HpV1MHWswCl5miFOuLoRVyIplB3qEJxZK2OA= github.com/hajimehoshi/go-mp3 v0.3.4 h1:NUP7pBYH8OguP4diaTZ9wJbUbk3tC0KlfzsEpWmYj68= From fbd067c96e6e54bac5559c4bddcbd566c4619230 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 18 May 2024 17:43:35 +0900 Subject: [PATCH 55/81] .github/workflows: fix tests for Linux 386 Updates #2667 Closes #2995 --- .github/workflows/test.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9f26b5bd9..d54493e66 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -142,8 +142,6 @@ jobs: run: | sudo dpkg --add-architecture i386 sudo apt-get update - sudo apt-get install ppa-purge - sudo ppa-purge -y ppa:ubuntu-toolchain-r/test # Hack for 32bit Linux (#2667) sudo apt-get install gcc-multilib sudo apt-get install libasound2-dev:i386 libgl1-mesa-dev:i386 libxcursor-dev:i386 libxi-dev:i386 libxinerama-dev:i386 libxrandr-dev:i386 libxxf86vm-dev:i386 env CGO_ENABLED=1 GOARCH=386 go test -shuffle=on -v -p=1 ./... From d2c58dac8c7a636f51603e8029b679b8604cfb0d Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 18 May 2024 20:09:28 +0900 Subject: [PATCH 56/81] audio/vorbis: add (*Stream).SampleRate Updates #2996 --- audio/vorbis/vorbis.go | 23 +++++++++++++++++------ audio/vorbis/vorbis_test.go | 26 +++++++++++++++----------- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/audio/vorbis/vorbis.go b/audio/vorbis/vorbis.go index 71381ab9d..919bda10e 100644 --- a/audio/vorbis/vorbis.go +++ b/audio/vorbis/vorbis.go @@ -27,8 +27,9 @@ import ( // Stream is a decoded audio stream. type Stream struct { - decoded io.ReadSeeker - size int64 + decoded io.ReadSeeker + size int64 + sampleRate int } // Read is implementation of io.Reader's Read. @@ -50,6 +51,11 @@ func (s *Stream) Length() int64 { return s.size } +// SampleRate returns the sample rate of the decoded stream. +func (s *Stream) SampleRate() int { + return s.sampleRate +} + type decoder interface { Read([]float32) (int, error) SetPosition(int64) error @@ -152,7 +158,7 @@ func decode(in io.Reader) (*decoded, int, int, error) { // A Stream doesn't close src even if src implements io.Closer. // Closing the source is src owner's responsibility. func DecodeWithoutResampling(src io.Reader) (*Stream, error) { - decoded, channelCount, _, err := decode(src) + decoded, channelCount, sampleRate, err := decode(src) if err != nil { return nil, err } @@ -166,8 +172,9 @@ func DecodeWithoutResampling(src io.Reader) (*Stream, error) { size *= 2 } stream := &Stream{ - decoded: s, - size: size, + decoded: s, + size: size, + sampleRate: sampleRate, } return stream, nil } @@ -204,7 +211,11 @@ func DecodeWithSampleRate(sampleRate int, src io.Reader) (*Stream, error) { s = r size = r.Length() } - stream := &Stream{decoded: s, size: size} + stream := &Stream{ + decoded: s, + size: size, + sampleRate: sampleRate, + } return stream, nil } diff --git a/audio/vorbis/vorbis_test.go b/audio/vorbis/vorbis_test.go index 97f2da280..ef028e333 100644 --- a/audio/vorbis/vorbis_test.go +++ b/audio/vorbis/vorbis_test.go @@ -50,16 +50,16 @@ func TestMono(t *testing.T) { } // Stream decoded by audio/vorbis.DecodeWithSampleRate() is always 16bit stereo. - got := s.Length() - // On the other hand, the original vorbis package is monoral. // As Length() represents the number of samples, // this needs to be doubled by 2 (= bytes in 16bits). - want := r.Length() * 2 * 2 - - if got != want { + if got, want := s.Length(), r.Length()*2*2; got != want { t.Errorf("s.Length(): got: %d, want: %d", got, want) } + + if got, want := s.SampleRate(), audioContext.SampleRate(); got != want { + t.Errorf("s.SampleRate(): got: %d, want: %d", got, want) + } } func TestTooShort(t *testing.T) { @@ -70,11 +70,13 @@ func TestTooShort(t *testing.T) { t.Fatal(err) } - got := s.Length() - want := int64(79424) - if got != want { + if got, want := s.Length(), int64(79424); got != want { t.Errorf("s.Length(): got: %d, want: %d", got, want) } + + if got, want := s.SampleRate(), audioContext.SampleRate(); got != want { + t.Errorf("s.SampleRate(): got: %d, want: %d", got, want) + } } type reader struct { @@ -93,9 +95,11 @@ func TestNonSeeker(t *testing.T) { t.Fatal(err) } - got := s.Length() - want := int64(0) - if got != want { + if got, want := s.Length(), int64(0); got != want { t.Errorf("s.Length(): got: %d, want: %d", got, want) } + + if got, want := s.SampleRate(), audioContext.SampleRate(); got != want { + t.Errorf("s.SampleRate(): got: %d, want: %d", got, want) + } } From ac83181403a423a36e57a50f9e2919e641c9e1e1 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 18 May 2024 20:13:36 +0900 Subject: [PATCH 57/81] audio/mp3: add (*Stream).SampleRate Updates #2996 --- audio/mp3/decode.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/audio/mp3/decode.go b/audio/mp3/decode.go index 05c7a177c..815be418f 100644 --- a/audio/mp3/decode.go +++ b/audio/mp3/decode.go @@ -31,6 +31,7 @@ import ( type Stream struct { orig *mp3.Decoder resampling *convert.Resampling + sampleRate int } // Read is implementation of io.Reader's Read. @@ -57,6 +58,11 @@ func (s *Stream) Length() int64 { return s.orig.Length() } +// SampleRate returns the sample rate of the decoded stream. +func (s *Stream) SampleRate() int { + return s.sampleRate +} + // DecodeWithoutResampling decodes an MP3 source and returns a decoded stream. // // DecodeWithoutResampling returns error when decoding fails or IO error happens. @@ -73,6 +79,7 @@ func DecodeWithoutResampling(src io.Reader) (*Stream, error) { s := &Stream{ orig: d, resampling: nil, + sampleRate: d.SampleRate(), } return s, nil } @@ -103,6 +110,7 @@ func DecodeWithSampleRate(sampleRate int, src io.Reader) (*Stream, error) { s := &Stream{ orig: d, resampling: r, + sampleRate: sampleRate, } return s, nil } From a612e7403154871a6ba8b26daaccaab76aeb9c52 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 18 May 2024 20:19:27 +0900 Subject: [PATCH 58/81] audio/wav: add (*Stream).SampleRate Closes #2996 --- audio/wav/decode.go | 59 +++++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/audio/wav/decode.go b/audio/wav/decode.go index abd8593b7..097795d3f 100644 --- a/audio/wav/decode.go +++ b/audio/wav/decode.go @@ -26,8 +26,9 @@ import ( // Stream is a decoded audio stream. type Stream struct { - inner io.ReadSeeker - size int64 + inner io.ReadSeeker + size int64 + sampleRate int } // Read is implementation of io.Reader's Read. @@ -49,6 +50,11 @@ func (s *Stream) Length() int64 { return s.size } +// SampleRate returns the sample rate of the decoded stream. +func (s *Stream) SampleRate() int { + return s.sampleRate +} + type stream struct { src io.Reader headerSize int64 @@ -114,7 +120,7 @@ func (s *stream) Seek(offset int64, whence int) (int64, error) { // A Stream doesn't close src even if src implements io.Closer. // Closing the source is src owner's responsibility. func DecodeWithoutResampling(src io.Reader) (*Stream, error) { - s, _, err := decode(src) + s, err := decode(src) if err != nil { return nil, err } @@ -138,36 +144,37 @@ func DecodeWithoutResampling(src io.Reader) (*Stream, error) { // Resampling can be a very heavy task. Stream has a cache for resampling, but the size is limited. // Do not expect that Stream has a resampling cache even after whole data is played. func DecodeWithSampleRate(sampleRate int, src io.Reader) (*Stream, error) { - s, origSampleRate, err := decode(src) + s, err := decode(src) if err != nil { return nil, err } - if sampleRate == origSampleRate { + if sampleRate == s.sampleRate { return s, nil } - r := convert.NewResampling(s.inner, s.size, origSampleRate, sampleRate) + r := convert.NewResampling(s.inner, s.size, s.sampleRate, sampleRate) return &Stream{ - inner: r, - size: r.Length(), + inner: r, + size: r.Length(), + sampleRate: sampleRate, }, nil } -func decode(src io.Reader) (*Stream, int, error) { +func decode(src io.Reader) (*Stream, error) { buf := make([]byte, 12) n, err := io.ReadFull(src, buf) if n != len(buf) { - return nil, 0, fmt.Errorf("wav: invalid header") + return nil, fmt.Errorf("wav: invalid header") } if err != nil { - return nil, 0, err + return nil, err } if !bytes.Equal(buf[0:4], []byte("RIFF")) { - return nil, 0, fmt.Errorf("wav: invalid header: 'RIFF' not found") + return nil, fmt.Errorf("wav: invalid header: 'RIFF' not found") } if !bytes.Equal(buf[8:12], []byte("WAVE")) { - return nil, 0, fmt.Errorf("wav: invalid header: 'WAVE' not found") + return nil, fmt.Errorf("wav: invalid header: 'WAVE' not found") } // Read chunks @@ -181,10 +188,10 @@ chunks: buf := make([]byte, 8) n, err := io.ReadFull(src, buf) if n != len(buf) { - return nil, 0, fmt.Errorf("wav: invalid header") + return nil, fmt.Errorf("wav: invalid header") } if err != nil { - return nil, 0, err + return nil, err } headerSize += 8 size := int64(buf[4]) | int64(buf[5])<<8 | int64(buf[6])<<16 | int64(buf[7])<<24 @@ -192,19 +199,19 @@ chunks: case bytes.Equal(buf[0:4], []byte("fmt ")): // Size of 'fmt' header is usually 16, but can be more than 16. if size < 16 { - return nil, 0, fmt.Errorf("wav: invalid header: maybe non-PCM file?") + return nil, fmt.Errorf("wav: invalid header: maybe non-PCM file?") } buf := make([]byte, size) n, err := io.ReadFull(src, buf) if n != len(buf) { - return nil, 0, fmt.Errorf("wav: invalid header") + return nil, fmt.Errorf("wav: invalid header") } if err != nil { - return nil, 0, err + return nil, err } format := int(buf[0]) | int(buf[1])<<8 if format != 1 { - return nil, 0, fmt.Errorf("wav: format must be linear PCM") + return nil, fmt.Errorf("wav: format must be linear PCM") } channelCount := int(buf[2]) | int(buf[3])<<8 switch channelCount { @@ -213,11 +220,11 @@ chunks: case 2: mono = false default: - return nil, 0, fmt.Errorf("wav: number of channels must be 1 or 2 but was %d", channelCount) + return nil, fmt.Errorf("wav: number of channels must be 1 or 2 but was %d", channelCount) } bitsPerSample = int(buf[14]) | int(buf[15])<<8 if bitsPerSample != 8 && bitsPerSample != 16 { - return nil, 0, fmt.Errorf("wav: bits per sample must be 8 or 16 but was %d", bitsPerSample) + return nil, fmt.Errorf("wav: bits per sample must be 8 or 16 but was %d", bitsPerSample) } sampleRate = int(buf[4]) | int(buf[5])<<8 | int(buf[6])<<16 | int(buf[7])<<24 headerSize += size @@ -228,10 +235,10 @@ chunks: buf := make([]byte, size) n, err := io.ReadFull(src, buf) if n != len(buf) { - return nil, 0, fmt.Errorf("wav: invalid header") + return nil, fmt.Errorf("wav: invalid header") } if err != nil { - return nil, 0, err + return nil, err } headerSize += size } @@ -252,7 +259,11 @@ chunks: dataSize *= 2 } } - return &Stream{inner: s, size: dataSize}, sampleRate, nil + return &Stream{ + inner: s, + size: dataSize, + sampleRate: sampleRate, + }, nil } // Decode decodes WAV (RIFF) data to playable stream. From 002e375d644aad9d110be69b3b4dac3a6d26713c Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 18 May 2024 22:15:24 +0900 Subject: [PATCH 59/81] examples/audio: remove unneeded resampling --- examples/audio/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/audio/main.go b/examples/audio/main.go index 0a2fdbdd1..5d8ca344b 100644 --- a/examples/audio/main.go +++ b/examples/audio/main.go @@ -171,7 +171,7 @@ func NewPlayer(game *Game, audioContext *audio.Context, musicType musicType) (*P player.audioPlayer.Play() go func() { - s, err := wav.DecodeWithSampleRate(sampleRate, bytes.NewReader(raudio.Jab_wav)) + s, err := wav.DecodeWithoutResampling(bytes.NewReader(raudio.Jab_wav)) if err != nil { log.Fatal(err) return From 657e04d3d142f4fb0c9cfa50e99a7d05f88f19ed Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 19 May 2024 18:01:58 +0900 Subject: [PATCH 60/81] internal/graphicsdriver/playstation5: separate the shader part --- .../playstation5/graphics_playstation5.go | 4 --- .../playstation5/shader_paystation5.go | 27 +++++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 internal/graphicsdriver/playstation5/shader_paystation5.go diff --git a/internal/graphicsdriver/playstation5/graphics_playstation5.go b/internal/graphicsdriver/playstation5/graphics_playstation5.go index 7ac2a3d3e..a9bc03279 100644 --- a/internal/graphicsdriver/playstation5/graphics_playstation5.go +++ b/internal/graphicsdriver/playstation5/graphics_playstation5.go @@ -153,7 +153,3 @@ func (s *Shader) ID() graphicsdriver.ShaderID { func (s *Shader) Dispose() { C.ebitengine_DisposeShader(C.int(s.id)) } - -func RegisterPrecompiledShaders(hash shaderir.SourceHash, vertex, pixel []byte) { - // TODO: Implement this. -} diff --git a/internal/graphicsdriver/playstation5/shader_paystation5.go b/internal/graphicsdriver/playstation5/shader_paystation5.go new file mode 100644 index 000000000..b3333fdf7 --- /dev/null +++ b/internal/graphicsdriver/playstation5/shader_paystation5.go @@ -0,0 +1,27 @@ +// Copyright 2023 The Ebitengine Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build playstation5 + +// This is a separated pure Go file so that the `shaderprecomp` package can use this without Cgo. + +package playstation5 + +import ( + "github.com/hajimehoshi/ebiten/v2/internal/shaderir" +) + +func RegisterPrecompiledShaders(hash shaderir.SourceHash, vertex, pixel []byte) { + // TODO: Implement this. +} From b0a4b6ebbfd90c110cffe5a6935965c76a750359 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 19 May 2024 23:01:47 +0900 Subject: [PATCH 61/81] internal/graphicsdriver/playstation5: update the copyright year --- internal/graphicsdriver/playstation5/shader_paystation5.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/graphicsdriver/playstation5/shader_paystation5.go b/internal/graphicsdriver/playstation5/shader_paystation5.go index b3333fdf7..8128f874c 100644 --- a/internal/graphicsdriver/playstation5/shader_paystation5.go +++ b/internal/graphicsdriver/playstation5/shader_paystation5.go @@ -1,4 +1,4 @@ -// Copyright 2023 The Ebitengine Authors +// Copyright 2024 The Ebitengine Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. From 5942192b66645d22d6e058586642fc8e58598afc Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Tue, 21 May 2024 02:34:54 +0900 Subject: [PATCH 62/81] audio/vorbis: refactoring --- audio/vorbis/vorbis.go | 115 +++++++++++++++++++---------------------- 1 file changed, 54 insertions(+), 61 deletions(-) diff --git a/audio/vorbis/vorbis.go b/audio/vorbis/vorbis.go index 919bda10e..6751c499d 100644 --- a/audio/vorbis/vorbis.go +++ b/audio/vorbis/vorbis.go @@ -27,28 +27,28 @@ import ( // Stream is a decoded audio stream. type Stream struct { - decoded io.ReadSeeker - size int64 + readSeeker io.ReadSeeker + length int64 sampleRate int } // Read is implementation of io.Reader's Read. func (s *Stream) Read(p []byte) (int, error) { - return s.decoded.Read(p) + return s.readSeeker.Read(p) } // Seek is implementation of io.Seeker's Seek. // // Note that Seek can take long since decoding is a relatively heavy task. func (s *Stream) Seek(offset int64, whence int) (int64, error) { - return s.decoded.Seek(offset, whence) + return s.readSeeker.Seek(offset, whence) } // Length returns the size of decoded stream in bytes. // // If the source is not io.Seeker, Length returns 0. func (s *Stream) Length() int64 { - return s.size + return s.length } // SampleRate returns the sample rate of the decoded stream. @@ -56,27 +56,19 @@ func (s *Stream) SampleRate() int { return s.sampleRate } -type decoder interface { - Read([]float32) (int, error) - SetPosition(int64) error - Length() int64 - Channels() int - SampleRate() int +type i16Stream struct { + totalBytes int + posInBytes int + vorbisReader *oggvorbis.Reader + i16Reader io.Reader } -type decoded struct { - totalBytes int - posInBytes int - decoder decoder - decoderr io.Reader -} - -func (d *decoded) Read(b []byte) (int, error) { - if d.decoderr == nil { - d.decoderr = convert.NewReaderFromFloat32Reader(d.decoder) +func (s *i16Stream) Read(b []byte) (int, error) { + if s.i16Reader == nil { + s.i16Reader = convert.NewReaderFromFloat32Reader(s.vorbisReader) } - l := d.totalBytes - d.posInBytes + l := s.totalBytes - s.posInBytes if l > len(b) { l = len(b) } @@ -85,7 +77,7 @@ func (d *decoded) Read(b []byte) (int, error) { } retry: - n, err := d.decoderr.Read(b[:l]) + n, err := s.i16Reader.Read(b[:l]) if err != nil && err != io.EOF { return 0, err } @@ -94,59 +86,63 @@ retry: goto retry } - d.posInBytes += n - if d.posInBytes == d.totalBytes || err == io.EOF { + s.posInBytes += n + if s.posInBytes == s.totalBytes || err == io.EOF { return n, io.EOF } return n, nil } -func (d *decoded) Seek(offset int64, whence int) (int64, error) { +func (s *i16Stream) Seek(offset int64, whence int) (int64, error) { next := int64(0) switch whence { case io.SeekStart: next = offset case io.SeekCurrent: - next = int64(d.posInBytes) + offset + next = int64(s.posInBytes) + offset case io.SeekEnd: - next = int64(d.totalBytes) + offset + next = int64(s.totalBytes) + offset } // pos should be always even next = next / 2 * 2 - d.posInBytes = int(next) - if err := d.decoder.SetPosition(next / int64(d.decoder.Channels()) / 2); err != nil { + s.posInBytes = int(next) + if err := s.vorbisReader.SetPosition(next / int64(s.vorbisReader.Channels()) / 2); err != nil { return 0, err } - d.decoderr = nil + s.i16Reader = nil return next, nil } -func (d *decoded) Length() int64 { - return int64(d.totalBytes) +func (s *i16Stream) Length() int64 { + return int64(s.totalBytes) } // decode accepts an ogg stream and returns a decorded stream. -func decode(in io.Reader) (*decoded, int, int, error) { +func decode(in io.Reader) (*i16Stream, int, int, error) { r, err := oggvorbis.NewReader(in) if err != nil { return nil, 0, 0, err } - d := &decoded{ + if r.Channels() != 1 && r.Channels() != 2 { + return nil, 0, 0, fmt.Errorf("vorbis: number of channels must be 1 or 2 but was %d", r.Channels()) + } + + s := &i16Stream{ // TODO: r.Length() returns 0 when the format is unknown. // Should we check that? - totalBytes: int(r.Length()) * r.Channels() * 2, // 2 means 16bit per sample. - posInBytes: 0, - decoder: r, + totalBytes: int(r.Length()) * r.Channels() * 2, // 2 means 16bit per sample. + posInBytes: 0, + vorbisReader: r, } if _, ok := in.(io.Seeker); ok { - if _, err := d.Read(make([]byte, 65536)); err != nil && err != io.EOF { + if _, err := s.Read(make([]byte, 65536)); err != nil && err != io.EOF { return nil, 0, 0, err } - if _, err := d.Seek(0, io.SeekStart); err != nil { + if _, err := s.Seek(0, io.SeekStart); err != nil { return nil, 0, 0, err } } - return d, r.Channels(), r.SampleRate(), nil + return s, r.Channels(), r.SampleRate(), nil } // DecodeWithoutResampling decodes Ogg/Vorbis data to playable stream. @@ -158,22 +154,21 @@ func decode(in io.Reader) (*decoded, int, int, error) { // A Stream doesn't close src even if src implements io.Closer. // Closing the source is src owner's responsibility. func DecodeWithoutResampling(src io.Reader) (*Stream, error) { - decoded, channelCount, sampleRate, err := decode(src) + i16Stream, channelCount, sampleRate, err := decode(src) if err != nil { return nil, err } - if channelCount != 1 && channelCount != 2 { - return nil, fmt.Errorf("vorbis: number of channels must be 1 or 2 but was %d", channelCount) - } - var s io.ReadSeeker = decoded - size := decoded.Length() + + var s io.ReadSeeker = i16Stream + length := i16Stream.Length() if channelCount == 1 { s = convert.NewStereo16(s, true, false) - size *= 2 + length *= 2 } + stream := &Stream{ - decoded: s, - size: size, + readSeeker: s, + length: length, sampleRate: sampleRate, } return stream, nil @@ -193,27 +188,25 @@ func DecodeWithoutResampling(src io.Reader) (*Stream, error) { // Resampling can be a very heavy task. Stream has a cache for resampling, but the size is limited. // Do not expect that Stream has a resampling cache even after whole data is played. func DecodeWithSampleRate(sampleRate int, src io.Reader) (*Stream, error) { - decoded, channelCount, origSampleRate, err := decode(src) + i16Stream, channelCount, origSampleRate, err := decode(src) if err != nil { return nil, err } - if channelCount != 1 && channelCount != 2 { - return nil, fmt.Errorf("vorbis: number of channels must be 1 or 2 but was %d", channelCount) - } - var s io.ReadSeeker = decoded - size := decoded.Length() + + var s io.ReadSeeker = i16Stream + length := i16Stream.Length() if channelCount == 1 { s = convert.NewStereo16(s, true, false) - size *= 2 + length *= 2 } if origSampleRate != sampleRate { - r := convert.NewResampling(s, size, origSampleRate, sampleRate) + r := convert.NewResampling(s, length, origSampleRate, sampleRate) s = r - size = r.Length() + length = r.Length() } stream := &Stream{ - decoded: s, - size: size, + readSeeker: s, + length: length, sampleRate: sampleRate, } return stream, nil From 38d28929063c8b69d5e520ade3833936f791f29a Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 26 May 2024 15:11:48 +0900 Subject: [PATCH 63/81] internal/glfw: bug fix: the cursor position was reset unexpectedly There was a mistake when updating GLFW to v3.3.9 at 4647e9de53bbe1c54dc125fd6e7dfc6aa16baddc. When the cursor mode is set to be enabled, the cursor position was unexpectedly reset. This change fixes the issue. Closes #2997 --- internal/glfw/win32_window_windows.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/glfw/win32_window_windows.go b/internal/glfw/win32_window_windows.go index 6fcb33356..081aff4fe 100644 --- a/internal/glfw/win32_window_windows.go +++ b/internal/glfw/win32_window_windows.go @@ -2309,7 +2309,7 @@ func (w *Window) platformSetCursorMode(mode int) error { if mode == CursorDisabled { _glfw.platformWindow.disabledCursorWindow = w - } else { + } else if _glfw.platformWindow.disabledCursorWindow == w { _glfw.platformWindow.disabledCursorWindow = nil if err := w.platformSetCursorPos(_glfw.platformWindow.restoreCursorPosX, _glfw.platformWindow.restoreCursorPosY); err != nil { return err From 13c75184004e33645d9ad1b6010279e28de2b70c Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 26 May 2024 15:55:46 +0900 Subject: [PATCH 64/81] all: update bitmapfont --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ccedbace8..d90c6dfa7 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/ebitengine/purego v0.8.0-alpha.2 github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f github.com/go-text/typesetting v0.1.1 - github.com/hajimehoshi/bitmapfont/v3 v3.1.0 + github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.1 github.com/hajimehoshi/go-mp3 v0.3.4 github.com/jakecoffman/cp v1.2.1 github.com/jezek/xgb v1.1.1 diff --git a/go.sum b/go.sum index 2805f8e14..aa95e3d6e 100644 --- a/go.sum +++ b/go.sum @@ -11,8 +11,8 @@ github.com/gen2brain/mpeg v0.3.2-0.20240412154320-a2ac4fc8a46f/go.mod h1:i/ebyRR github.com/go-text/typesetting v0.1.1 h1:bGAesCuo85nXnEN5LmFMVGAGpGkCPtHrZLi//qD7EJo= github.com/go-text/typesetting v0.1.1/go.mod h1:d22AnmeKq/on0HNv73UFriMKc4Ez6EqZAofLhAzpSzI= github.com/go-text/typesetting-utils v0.0.0-20231211103740-d9332ae51f04 h1:zBx+p/W2aQYtNuyZNcTfinWvXBQwYtDfme051PR/lAY= -github.com/hajimehoshi/bitmapfont/v3 v3.1.0 h1:JLy/na2e83GewqebpFbS2LHpDVnGdzmyJOpqXtBgLm0= -github.com/hajimehoshi/bitmapfont/v3 v3.1.0/go.mod h1:VVaVK/4HpV1MHWswCl5miFOuLoRVyIplB3qEJxZK2OA= +github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.1 h1:GLoMNCbvXOd39tFkqk9w/MI0xSLJaDzEOOl8mT1ILtI= +github.com/hajimehoshi/bitmapfont/v3 v3.2.0-alpha.1/go.mod h1:VVaVK/4HpV1MHWswCl5miFOuLoRVyIplB3qEJxZK2OA= github.com/hajimehoshi/go-mp3 v0.3.4 h1:NUP7pBYH8OguP4diaTZ9wJbUbk3tC0KlfzsEpWmYj68= github.com/hajimehoshi/go-mp3 v0.3.4/go.mod h1:fRtZraRFcWb0pu7ok0LqyFhCUrPeMsGRSVop0eemFmo= github.com/hajimehoshi/oto/v2 v2.3.1/go.mod h1:seWLbgHH7AyUMYKfKYT9pg7PhUu9/SisyJvNTT+ASQo= From 1c438cb5c841b7476ae18c24598b8047cb3dd207 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 26 May 2024 16:05:58 +0900 Subject: [PATCH 65/81] examples/shaderprecomp/fxc, examples/shaderprecomp/metallib: add go:build --- examples/shaderprecomp/fxc/generate.go | 2 ++ examples/shaderprecomp/metallib/generate.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/examples/shaderprecomp/fxc/generate.go b/examples/shaderprecomp/fxc/generate.go index 772999805..601e6532c 100644 --- a/examples/shaderprecomp/fxc/generate.go +++ b/examples/shaderprecomp/fxc/generate.go @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build windows + //go:generate go run gen.go package fxc diff --git a/examples/shaderprecomp/metallib/generate.go b/examples/shaderprecomp/metallib/generate.go index efd32a56d..658ccc004 100644 --- a/examples/shaderprecomp/metallib/generate.go +++ b/examples/shaderprecomp/metallib/generate.go @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +//go:build darwin + //go:generate go run gen.go package metallib From ecc3f29af1cdbf475f6637b12d368be18b586eab Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 26 May 2024 19:24:31 +0900 Subject: [PATCH 66/81] shaderprecomp: accept an ID instead of source to register Updates #2861 --- examples/shaderprecomp/fxc/gen.go | 2 +- examples/shaderprecomp/register_darwin.go | 39 +++++++++++----- examples/shaderprecomp/register_windows.go | 52 +++++++++++++--------- internal/shaderir/program.go | 14 ++++++ shaderprecomp/shaderprecomp.go | 9 ++++ shaderprecomp/shaderprecomp_darwin.go | 6 +-- shaderprecomp/shaderprecomp_windows.go | 6 +-- 7 files changed, 87 insertions(+), 41 deletions(-) diff --git a/examples/shaderprecomp/fxc/gen.go b/examples/shaderprecomp/fxc/gen.go index 7a6457469..8f4670d3b 100644 --- a/examples/shaderprecomp/fxc/gen.go +++ b/examples/shaderprecomp/fxc/gen.go @@ -40,7 +40,7 @@ func run() error { if errors.Is(err, exec.ErrNotFound) { fmt.Fprintln(os.Stderr, "fxc.exe not found. Please install Windows SDK.") fmt.Fprintln(os.Stderr, "See https://learn.microsoft.com/en-us/windows/win32/direct3dtools/fxc for more details.") - fmt.Fprintln(os.Stderr, "On PowerShell, you can add a path to the PATH environment variable temporarily like:") + fmt.Fprintln(os.Stderr, "HINT: On PowerShell, you can add a path to the PATH environment variable temporarily like:") fmt.Fprintln(os.Stderr) fmt.Fprintln(os.Stderr, ` & (Get-Process -Id $PID).Path { $env:PATH="C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64;"+$env:PATH; go generate .\examples\shaderprecomp\fxc\ }`) fmt.Fprintln(os.Stderr) diff --git a/examples/shaderprecomp/register_darwin.go b/examples/shaderprecomp/register_darwin.go index b5b250cfd..5f00b0120 100644 --- a/examples/shaderprecomp/register_darwin.go +++ b/examples/shaderprecomp/register_darwin.go @@ -16,10 +16,9 @@ package main import ( "embed" - "errors" "fmt" - "io/fs" "os" + "strings" "github.com/hajimehoshi/ebiten/v2/shaderprecomp" ) @@ -28,24 +27,40 @@ import ( var metallibs embed.FS func registerPrecompiledShaders() error { - srcs := shaderprecomp.AppendBuildinShaderSources(nil) - defaultShaderSource, err := shaderprecomp.NewShaderSource(defaultShaderSourceBytes) + ents, err := metallibs.ReadDir("metallib") if err != nil { return err } - srcs = append(srcs, defaultShaderSource) - for _, src := range srcs { - name := src.ID().String() + ".metallib" + var registered bool + for _, ent := range ents { + if ent.IsDir() { + continue + } + + const suffix = ".metallib" + name := ent.Name() + if !strings.HasSuffix(name, suffix) { + continue + } + + id := name[:len(name)-len(suffix)] + srcID, err := shaderprecomp.ParseSourceID(id) + if err != nil { + continue + } + lib, err := metallibs.ReadFile("metallib/" + name) if err != nil { - if errors.Is(err, fs.ErrNotExist) { - fmt.Fprintf(os.Stderr, "precompiled Metal library %s was not found. Run 'go generate' for 'metallib' directory to generate them.\n", name) - continue - } return err } - shaderprecomp.RegisterMetalLibrary(src, lib) + + shaderprecomp.RegisterMetalLibrary(srcID, lib) + registered = true + } + + if !registered { + fmt.Fprintln(os.Stderr, "precompiled Metal libraries were not found. Run 'go generate' for 'metallib' directory to generate them.") } return nil diff --git a/examples/shaderprecomp/register_windows.go b/examples/shaderprecomp/register_windows.go index 34ac7437a..85a739b5c 100644 --- a/examples/shaderprecomp/register_windows.go +++ b/examples/shaderprecomp/register_windows.go @@ -16,10 +16,9 @@ package main import ( "embed" - "errors" "fmt" - "io/fs" "os" + "strings" "github.com/hajimehoshi/ebiten/v2/shaderprecomp" ) @@ -30,35 +29,44 @@ import ( var fxcs embed.FS func registerPrecompiledShaders() error { - srcs := shaderprecomp.AppendBuildinShaderSources(nil) - defaultShaderSource, err := shaderprecomp.NewShaderSource(defaultShaderSourceBytes) + ents, err := fxcs.ReadDir("fxc") if err != nil { return err } - srcs = append(srcs, defaultShaderSource) - for _, src := range srcs { - vsname := src.ID().String() + "_vs.fxc" - vs, err := fxcs.ReadFile("fxc/" + vsname) + var registered bool + for _, ent := range ents { + if ent.IsDir() { + continue + } + + const suffix = "_vs.fxc" + name := ent.Name() + if !strings.HasSuffix(name, suffix) { + continue + } + + id := name[:len(name)-len(suffix)] + srcID, err := shaderprecomp.ParseSourceID(id) + if err != nil { + continue + } + + vs, err := fxcs.ReadFile("fxc/" + id + "_vs.fxc") + if err != nil { + return err + } + ps, err := fxcs.ReadFile("fxc/" + id + "_ps.fxc") if err != nil { - if errors.Is(err, fs.ErrNotExist) { - fmt.Fprintf(os.Stderr, "precompiled HLSL library %s was not found. Run 'go generate' for 'fxc' directory to generate them.\n", vsname) - continue - } return err } - psname := src.ID().String() + "_ps.fxc" - ps, err := fxcs.ReadFile("fxc/" + psname) - if err != nil { - if errors.Is(err, fs.ErrNotExist) { - fmt.Fprintf(os.Stderr, "precompiled HLSL library %s was not found. Run 'go generate' for 'fxc' directory to generate them.\n", psname) - continue - } - return err - } + shaderprecomp.RegisterFXCs(srcID, vs, ps) + registered = true + } - shaderprecomp.RegisterFXCs(src, vs, ps) + if !registered { + fmt.Fprintln(os.Stderr, "precompiled HLSL libraries were not found. Run 'go generate' for 'fxc' directory to generate them.") } return nil diff --git a/internal/shaderir/program.go b/internal/shaderir/program.go index 8688057cb..f50725622 100644 --- a/internal/shaderir/program.go +++ b/internal/shaderir/program.go @@ -17,6 +17,7 @@ package shaderir import ( "encoding/hex" + "fmt" "go/constant" "go/token" "hash/fnv" @@ -42,6 +43,19 @@ func CalcSourceHash(source []byte) SourceHash { return hash } +func ParseSourceHash(s string) (SourceHash, error) { + bs, err := hex.DecodeString(s) + if err != nil { + return SourceHash{}, err + } + var hash SourceHash + if len(bs) != len(hash) { + return SourceHash{}, fmt.Errorf("shaderir: invalid size hash: %s", s) + } + copy(hash[:], bs) + return hash, nil +} + func (s SourceHash) String() string { return hex.EncodeToString(s[:]) } diff --git a/shaderprecomp/shaderprecomp.go b/shaderprecomp/shaderprecomp.go index 650ebc1b5..f761d695e 100644 --- a/shaderprecomp/shaderprecomp.go +++ b/shaderprecomp/shaderprecomp.go @@ -64,6 +64,15 @@ func (s *ShaderSource) ID() ShaderSourceID { // ShaderSourceID is a uniuqe identifier for a shader source. type ShaderSourceID [16]byte +// ParseSourceID parses a string representation of the shader source ID. +func ParseSourceID(s string) (ShaderSourceID, error) { + h, err := shaderir.ParseSourceHash(s) + if err != nil { + return ShaderSourceID{}, err + } + return ShaderSourceID(h), nil +} + // String returns a string representation of the shader source ID. func (s ShaderSourceID) String() string { return shaderir.SourceHash(s).String() diff --git a/shaderprecomp/shaderprecomp_darwin.go b/shaderprecomp/shaderprecomp_darwin.go index b795ba685..796efb690 100644 --- a/shaderprecomp/shaderprecomp_darwin.go +++ b/shaderprecomp/shaderprecomp_darwin.go @@ -39,11 +39,11 @@ func CompileToMSL(w io.Writer, source *ShaderSource) error { return nil } -// RegisterMetalLibrary registers a precompiled Metal library for a shader source. +// RegisterMetalLibrary registers a precompiled Metal library for a shader source ID. // library must be the content of a .metallib file. // For more details, see https://developer.apple.com/documentation/metal/shader_libraries/building_a_shader_library_by_precompiling_source_files. // // RegisterMetalLibrary is concurrent-safe. -func RegisterMetalLibrary(source *ShaderSource, library []byte) { - metal.RegisterPrecompiledLibrary(shaderir.SourceHash(source.ID()), library) +func RegisterMetalLibrary(id ShaderSourceID, library []byte) { + metal.RegisterPrecompiledLibrary(shaderir.SourceHash(id), library) } diff --git a/shaderprecomp/shaderprecomp_windows.go b/shaderprecomp/shaderprecomp_windows.go index 27c9de404..7bdea4a48 100644 --- a/shaderprecomp/shaderprecomp_windows.go +++ b/shaderprecomp/shaderprecomp_windows.go @@ -57,11 +57,11 @@ func CompileToHLSL(vertexWriter, pixelWriter io.Writer, source *ShaderSource) er return nil } -// RegisterFXCs registers a precompiled HLSL (FXC) for a shader source. +// RegisterFXCs registers a precompiled HLSL (FXC) for a shader source ID. // vertexFXC and pixelFXC must be the content of .fxc files generated by `fxc` command. // For more details, see https://learn.microsoft.com/en-us/windows/win32/direct3dtools/dx-graphics-tools-fxc-using. // // RegisterFXCs is concurrent-safe. -func RegisterFXCs(source *ShaderSource, vertexFXC, pixelFXC []byte) { - directx.RegisterPrecompiledFXCs(shaderir.SourceHash(source.ID()), vertexFXC, pixelFXC) +func RegisterFXCs(id ShaderSourceID, vertexFXC, pixelFXC []byte) { + directx.RegisterPrecompiledFXCs(shaderir.SourceHash(id), vertexFXC, pixelFXC) } From 3279688dd6b09816ed72f747f82bf4afe48a7270 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 26 May 2024 21:29:28 +0900 Subject: [PATCH 67/81] Revert "shaderprecomp: accept an ID instead of source to register" This reverts commit ecc3f29af1cdbf475f6637b12d368be18b586eab. Reason: we are considering to remove ShaderSourceID Updates #2861 Updates #2999 --- examples/shaderprecomp/fxc/gen.go | 2 +- examples/shaderprecomp/register_darwin.go | 39 +++++----------- examples/shaderprecomp/register_windows.go | 52 +++++++++------------- internal/shaderir/program.go | 14 ------ shaderprecomp/shaderprecomp.go | 9 ---- shaderprecomp/shaderprecomp_darwin.go | 6 +-- shaderprecomp/shaderprecomp_windows.go | 6 +-- 7 files changed, 41 insertions(+), 87 deletions(-) diff --git a/examples/shaderprecomp/fxc/gen.go b/examples/shaderprecomp/fxc/gen.go index 8f4670d3b..7a6457469 100644 --- a/examples/shaderprecomp/fxc/gen.go +++ b/examples/shaderprecomp/fxc/gen.go @@ -40,7 +40,7 @@ func run() error { if errors.Is(err, exec.ErrNotFound) { fmt.Fprintln(os.Stderr, "fxc.exe not found. Please install Windows SDK.") fmt.Fprintln(os.Stderr, "See https://learn.microsoft.com/en-us/windows/win32/direct3dtools/fxc for more details.") - fmt.Fprintln(os.Stderr, "HINT: On PowerShell, you can add a path to the PATH environment variable temporarily like:") + fmt.Fprintln(os.Stderr, "On PowerShell, you can add a path to the PATH environment variable temporarily like:") fmt.Fprintln(os.Stderr) fmt.Fprintln(os.Stderr, ` & (Get-Process -Id $PID).Path { $env:PATH="C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64;"+$env:PATH; go generate .\examples\shaderprecomp\fxc\ }`) fmt.Fprintln(os.Stderr) diff --git a/examples/shaderprecomp/register_darwin.go b/examples/shaderprecomp/register_darwin.go index 5f00b0120..b5b250cfd 100644 --- a/examples/shaderprecomp/register_darwin.go +++ b/examples/shaderprecomp/register_darwin.go @@ -16,9 +16,10 @@ package main import ( "embed" + "errors" "fmt" + "io/fs" "os" - "strings" "github.com/hajimehoshi/ebiten/v2/shaderprecomp" ) @@ -27,40 +28,24 @@ import ( var metallibs embed.FS func registerPrecompiledShaders() error { - ents, err := metallibs.ReadDir("metallib") + srcs := shaderprecomp.AppendBuildinShaderSources(nil) + defaultShaderSource, err := shaderprecomp.NewShaderSource(defaultShaderSourceBytes) if err != nil { return err } + srcs = append(srcs, defaultShaderSource) - var registered bool - for _, ent := range ents { - if ent.IsDir() { - continue - } - - const suffix = ".metallib" - name := ent.Name() - if !strings.HasSuffix(name, suffix) { - continue - } - - id := name[:len(name)-len(suffix)] - srcID, err := shaderprecomp.ParseSourceID(id) - if err != nil { - continue - } - + for _, src := range srcs { + name := src.ID().String() + ".metallib" lib, err := metallibs.ReadFile("metallib/" + name) if err != nil { + if errors.Is(err, fs.ErrNotExist) { + fmt.Fprintf(os.Stderr, "precompiled Metal library %s was not found. Run 'go generate' for 'metallib' directory to generate them.\n", name) + continue + } return err } - - shaderprecomp.RegisterMetalLibrary(srcID, lib) - registered = true - } - - if !registered { - fmt.Fprintln(os.Stderr, "precompiled Metal libraries were not found. Run 'go generate' for 'metallib' directory to generate them.") + shaderprecomp.RegisterMetalLibrary(src, lib) } return nil diff --git a/examples/shaderprecomp/register_windows.go b/examples/shaderprecomp/register_windows.go index 85a739b5c..34ac7437a 100644 --- a/examples/shaderprecomp/register_windows.go +++ b/examples/shaderprecomp/register_windows.go @@ -16,9 +16,10 @@ package main import ( "embed" + "errors" "fmt" + "io/fs" "os" - "strings" "github.com/hajimehoshi/ebiten/v2/shaderprecomp" ) @@ -29,44 +30,35 @@ import ( var fxcs embed.FS func registerPrecompiledShaders() error { - ents, err := fxcs.ReadDir("fxc") + srcs := shaderprecomp.AppendBuildinShaderSources(nil) + defaultShaderSource, err := shaderprecomp.NewShaderSource(defaultShaderSourceBytes) if err != nil { return err } + srcs = append(srcs, defaultShaderSource) - var registered bool - for _, ent := range ents { - if ent.IsDir() { - continue - } - - const suffix = "_vs.fxc" - name := ent.Name() - if !strings.HasSuffix(name, suffix) { - continue - } - - id := name[:len(name)-len(suffix)] - srcID, err := shaderprecomp.ParseSourceID(id) - if err != nil { - continue - } - - vs, err := fxcs.ReadFile("fxc/" + id + "_vs.fxc") - if err != nil { - return err - } - ps, err := fxcs.ReadFile("fxc/" + id + "_ps.fxc") + for _, src := range srcs { + vsname := src.ID().String() + "_vs.fxc" + vs, err := fxcs.ReadFile("fxc/" + vsname) if err != nil { + if errors.Is(err, fs.ErrNotExist) { + fmt.Fprintf(os.Stderr, "precompiled HLSL library %s was not found. Run 'go generate' for 'fxc' directory to generate them.\n", vsname) + continue + } return err } - shaderprecomp.RegisterFXCs(srcID, vs, ps) - registered = true - } + psname := src.ID().String() + "_ps.fxc" + ps, err := fxcs.ReadFile("fxc/" + psname) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + fmt.Fprintf(os.Stderr, "precompiled HLSL library %s was not found. Run 'go generate' for 'fxc' directory to generate them.\n", psname) + continue + } + return err + } - if !registered { - fmt.Fprintln(os.Stderr, "precompiled HLSL libraries were not found. Run 'go generate' for 'fxc' directory to generate them.") + shaderprecomp.RegisterFXCs(src, vs, ps) } return nil diff --git a/internal/shaderir/program.go b/internal/shaderir/program.go index f50725622..8688057cb 100644 --- a/internal/shaderir/program.go +++ b/internal/shaderir/program.go @@ -17,7 +17,6 @@ package shaderir import ( "encoding/hex" - "fmt" "go/constant" "go/token" "hash/fnv" @@ -43,19 +42,6 @@ func CalcSourceHash(source []byte) SourceHash { return hash } -func ParseSourceHash(s string) (SourceHash, error) { - bs, err := hex.DecodeString(s) - if err != nil { - return SourceHash{}, err - } - var hash SourceHash - if len(bs) != len(hash) { - return SourceHash{}, fmt.Errorf("shaderir: invalid size hash: %s", s) - } - copy(hash[:], bs) - return hash, nil -} - func (s SourceHash) String() string { return hex.EncodeToString(s[:]) } diff --git a/shaderprecomp/shaderprecomp.go b/shaderprecomp/shaderprecomp.go index f761d695e..650ebc1b5 100644 --- a/shaderprecomp/shaderprecomp.go +++ b/shaderprecomp/shaderprecomp.go @@ -64,15 +64,6 @@ func (s *ShaderSource) ID() ShaderSourceID { // ShaderSourceID is a uniuqe identifier for a shader source. type ShaderSourceID [16]byte -// ParseSourceID parses a string representation of the shader source ID. -func ParseSourceID(s string) (ShaderSourceID, error) { - h, err := shaderir.ParseSourceHash(s) - if err != nil { - return ShaderSourceID{}, err - } - return ShaderSourceID(h), nil -} - // String returns a string representation of the shader source ID. func (s ShaderSourceID) String() string { return shaderir.SourceHash(s).String() diff --git a/shaderprecomp/shaderprecomp_darwin.go b/shaderprecomp/shaderprecomp_darwin.go index 796efb690..b795ba685 100644 --- a/shaderprecomp/shaderprecomp_darwin.go +++ b/shaderprecomp/shaderprecomp_darwin.go @@ -39,11 +39,11 @@ func CompileToMSL(w io.Writer, source *ShaderSource) error { return nil } -// RegisterMetalLibrary registers a precompiled Metal library for a shader source ID. +// RegisterMetalLibrary registers a precompiled Metal library for a shader source. // library must be the content of a .metallib file. // For more details, see https://developer.apple.com/documentation/metal/shader_libraries/building_a_shader_library_by_precompiling_source_files. // // RegisterMetalLibrary is concurrent-safe. -func RegisterMetalLibrary(id ShaderSourceID, library []byte) { - metal.RegisterPrecompiledLibrary(shaderir.SourceHash(id), library) +func RegisterMetalLibrary(source *ShaderSource, library []byte) { + metal.RegisterPrecompiledLibrary(shaderir.SourceHash(source.ID()), library) } diff --git a/shaderprecomp/shaderprecomp_windows.go b/shaderprecomp/shaderprecomp_windows.go index 7bdea4a48..27c9de404 100644 --- a/shaderprecomp/shaderprecomp_windows.go +++ b/shaderprecomp/shaderprecomp_windows.go @@ -57,11 +57,11 @@ func CompileToHLSL(vertexWriter, pixelWriter io.Writer, source *ShaderSource) er return nil } -// RegisterFXCs registers a precompiled HLSL (FXC) for a shader source ID. +// RegisterFXCs registers a precompiled HLSL (FXC) for a shader source. // vertexFXC and pixelFXC must be the content of .fxc files generated by `fxc` command. // For more details, see https://learn.microsoft.com/en-us/windows/win32/direct3dtools/dx-graphics-tools-fxc-using. // // RegisterFXCs is concurrent-safe. -func RegisterFXCs(id ShaderSourceID, vertexFXC, pixelFXC []byte) { - directx.RegisterPrecompiledFXCs(shaderir.SourceHash(id), vertexFXC, pixelFXC) +func RegisterFXCs(source *ShaderSource, vertexFXC, pixelFXC []byte) { + directx.RegisterPrecompiledFXCs(shaderir.SourceHash(source.ID()), vertexFXC, pixelFXC) } From 8be3bb41d5c3c432115264a07bb7fd794e478004 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 26 May 2024 21:40:11 +0900 Subject: [PATCH 68/81] shaderprecomp: remove `ShaderSource` and `ShaderSourceID` This change simplifies the APIs to avoid some confusions around IDs. Updates #2861 Closes #2999 --- examples/shaderprecomp/fxc/gen.go | 22 ++++----- examples/shaderprecomp/metallib/gen.go | 16 +++---- examples/shaderprecomp/register_darwin.go | 16 ++++--- examples/shaderprecomp/register_windows.go | 18 +++++--- .../graphicsdriver/directx/shader_windows.go | 4 +- .../graphicsdriver/metal/shader_darwin.go | 4 +- shaderprecomp/shaderprecomp.go | 46 +------------------ shaderprecomp/shaderprecomp_darwin.go | 9 ++-- shaderprecomp/shaderprecomp_windows.go | 9 ++-- 9 files changed, 53 insertions(+), 91 deletions(-) diff --git a/examples/shaderprecomp/fxc/gen.go b/examples/shaderprecomp/fxc/gen.go index 7a6457469..b48a90695 100644 --- a/examples/shaderprecomp/fxc/gen.go +++ b/examples/shaderprecomp/fxc/gen.go @@ -20,8 +20,10 @@ package main import ( + "encoding/hex" "errors" "fmt" + "hash/fnv" "os" "os/exec" "path/filepath" @@ -57,11 +59,7 @@ func run() error { srcs := shaderprecomp.AppendBuildinShaderSources(nil) - defaultSrcBytes, err := os.ReadFile(filepath.Join("..", "defaultshader.go")) - if err != nil { - return err - } - defaultSrc, err := shaderprecomp.NewShaderSource(defaultSrcBytes) + defaultSrc, err := os.ReadFile(filepath.Join("..", "defaultshader.go")) if err != nil { return err } @@ -77,9 +75,7 @@ func run() error { return nil } -func generateHSLSFiles(source *shaderprecomp.ShaderSource, tmpdir string) (vs, ps string, err error) { - id := source.ID().String() - +func generateHSLSFiles(source []byte, id string, tmpdir string) (vs, ps string, err error) { vsHLSLFilePath := filepath.Join(tmpdir, id+"_vs.hlsl") vsf, err := os.Create(vsHLSLFilePath) if err != nil { @@ -101,16 +97,18 @@ func generateHSLSFiles(source *shaderprecomp.ShaderSource, tmpdir string) (vs, p return vsHLSLFilePath, psHLSLFilePath, nil } -func compile(source *shaderprecomp.ShaderSource, tmpdir string) error { +func compile(kageSource []byte, tmpdir string) error { + h := fnv.New32() + _, _ = h.Write(kageSource) + id := hex.EncodeToString(h.Sum(nil)) + // Generate HLSL files. Make sure this process doesn't have any handlers of the files. // Without closing the files, fxc.exe cannot access the files. - vsHLSLFilePath, psHLSLFilePath, err := generateHSLSFiles(source, tmpdir) + vsHLSLFilePath, psHLSLFilePath, err := generateHSLSFiles(kageSource, id, tmpdir) if err != nil { return err } - id := source.ID().String() - vsFXCFilePath := id + "_vs.fxc" cmd := exec.Command("fxc.exe", "/nologo", "/O3", "/T", shaderprecomp.HLSLVertexShaderProfile, "/E", shaderprecomp.HLSLVertexShaderEntryPoint, "/Fo", vsFXCFilePath, vsHLSLFilePath) cmd.Stdout = os.Stdout diff --git a/examples/shaderprecomp/metallib/gen.go b/examples/shaderprecomp/metallib/gen.go index 1804c9da9..38027d0d2 100644 --- a/examples/shaderprecomp/metallib/gen.go +++ b/examples/shaderprecomp/metallib/gen.go @@ -20,6 +20,8 @@ package main import ( + "encoding/hex" + "hash/fnv" "os" "os/exec" "path/filepath" @@ -42,11 +44,7 @@ func run() error { srcs := shaderprecomp.AppendBuildinShaderSources(nil) - defaultSrcBytes, err := os.ReadFile(filepath.Join("..", "defaultshader.go")) - if err != nil { - return err - } - defaultSrc, err := shaderprecomp.NewShaderSource(defaultSrcBytes) + defaultSrc, err := os.ReadFile(filepath.Join("..", "defaultshader.go")) if err != nil { return err } @@ -62,8 +60,10 @@ func run() error { return nil } -func compile(source *shaderprecomp.ShaderSource, tmpdir string) error { - id := source.ID().String() +func compile(kageSource []byte, tmpdir string) error { + h := fnv.New32() + _, _ = h.Write(kageSource) + id := hex.EncodeToString(h.Sum(nil)) metalFilePath := filepath.Join(tmpdir, id+".metal") @@ -73,7 +73,7 @@ func compile(source *shaderprecomp.ShaderSource, tmpdir string) error { } defer f.Close() - if err := shaderprecomp.CompileToMSL(f, source); err != nil { + if err := shaderprecomp.CompileToMSL(f, kageSource); err != nil { return err } if err := f.Sync(); err != nil { diff --git a/examples/shaderprecomp/register_darwin.go b/examples/shaderprecomp/register_darwin.go index b5b250cfd..96a63c243 100644 --- a/examples/shaderprecomp/register_darwin.go +++ b/examples/shaderprecomp/register_darwin.go @@ -16,8 +16,10 @@ package main import ( "embed" + "encoding/hex" "errors" "fmt" + "hash/fnv" "io/fs" "os" @@ -29,14 +31,16 @@ var metallibs embed.FS func registerPrecompiledShaders() error { srcs := shaderprecomp.AppendBuildinShaderSources(nil) - defaultShaderSource, err := shaderprecomp.NewShaderSource(defaultShaderSourceBytes) - if err != nil { - return err - } - srcs = append(srcs, defaultShaderSource) + srcs = append(srcs, defaultShaderSourceBytes) for _, src := range srcs { - name := src.ID().String() + ".metallib" + // Calculate the hash of the source code to identify the Metal library. + // FNV is used as it is fast and the hash does not need to be secure. + h := fnv.New32() + _, _ = h.Write(src) + id := hex.EncodeToString(h.Sum(nil)) + + name := id + ".metallib" lib, err := metallibs.ReadFile("metallib/" + name) if err != nil { if errors.Is(err, fs.ErrNotExist) { diff --git a/examples/shaderprecomp/register_windows.go b/examples/shaderprecomp/register_windows.go index 34ac7437a..97db70188 100644 --- a/examples/shaderprecomp/register_windows.go +++ b/examples/shaderprecomp/register_windows.go @@ -16,8 +16,10 @@ package main import ( "embed" + "encoding/hex" "errors" "fmt" + "hash/fnv" "io/fs" "os" @@ -31,14 +33,16 @@ var fxcs embed.FS func registerPrecompiledShaders() error { srcs := shaderprecomp.AppendBuildinShaderSources(nil) - defaultShaderSource, err := shaderprecomp.NewShaderSource(defaultShaderSourceBytes) - if err != nil { - return err - } - srcs = append(srcs, defaultShaderSource) + srcs = append(srcs, defaultShaderSourceBytes) for _, src := range srcs { - vsname := src.ID().String() + "_vs.fxc" + // Calculate the hash of the source code to identify the Metal library. + // FNV is used as it is fast and the hash does not need to be secure. + h := fnv.New32() + _, _ = h.Write(src) + id := hex.EncodeToString(h.Sum(nil)) + + vsname := id + "_vs.fxc" vs, err := fxcs.ReadFile("fxc/" + vsname) if err != nil { if errors.Is(err, fs.ErrNotExist) { @@ -48,7 +52,7 @@ func registerPrecompiledShaders() error { return err } - psname := src.ID().String() + "_ps.fxc" + psname := id + "_ps.fxc" ps, err := fxcs.ReadFile("fxc/" + psname) if err != nil { if errors.Is(err, fs.ErrNotExist) { diff --git a/internal/graphicsdriver/directx/shader_windows.go b/internal/graphicsdriver/directx/shader_windows.go index 172ec1638..9127ddd6e 100644 --- a/internal/graphicsdriver/directx/shader_windows.go +++ b/internal/graphicsdriver/directx/shader_windows.go @@ -70,8 +70,8 @@ func (c *precompiledFXCs) get(hash shaderir.SourceHash) ([]byte, []byte) { var thePrecompiledFXCs precompiledFXCs -func RegisterPrecompiledFXCs(hash shaderir.SourceHash, vertex, pixel []byte) { - thePrecompiledFXCs.put(hash, vertex, pixel) +func RegisterPrecompiledFXCs(kageSource []byte, vertex, pixel []byte) { + thePrecompiledFXCs.put(shaderir.CalcSourceHash(kageSource), vertex, pixel) } var vertexShaderCache = map[string]*_ID3DBlob{} diff --git a/internal/graphicsdriver/metal/shader_darwin.go b/internal/graphicsdriver/metal/shader_darwin.go index 2ec9c9934..64079124e 100644 --- a/internal/graphicsdriver/metal/shader_darwin.go +++ b/internal/graphicsdriver/metal/shader_darwin.go @@ -51,8 +51,8 @@ func (c *precompiledLibraries) get(hash shaderir.SourceHash) []byte { var thePrecompiledLibraries precompiledLibraries -func RegisterPrecompiledLibrary(hash shaderir.SourceHash, bin []byte) { - thePrecompiledLibraries.put(hash, bin) +func RegisterPrecompiledLibrary(kageSource []byte, bin []byte) { + thePrecompiledLibraries.put(shaderir.CalcSourceHash(kageSource), bin) } type shaderRpsKey struct { diff --git a/shaderprecomp/shaderprecomp.go b/shaderprecomp/shaderprecomp.go index 650ebc1b5..d9cd6398f 100644 --- a/shaderprecomp/shaderprecomp.go +++ b/shaderprecomp/shaderprecomp.go @@ -16,8 +16,6 @@ package shaderprecomp import ( "github.com/hajimehoshi/ebiten/v2/internal/builtinshader" - "github.com/hajimehoshi/ebiten/v2/internal/graphics" - "github.com/hajimehoshi/ebiten/v2/internal/shaderir" ) // AppendBuildinShaderSources appends all the built-in shader sources to the given slice. @@ -25,46 +23,6 @@ import ( // Do not modify the content of the shader source. // // AppendBuildinShaderSources is concurrent-safe. -func AppendBuildinShaderSources(sources []*ShaderSource) []*ShaderSource { - for _, s := range builtinshader.AppendShaderSources(nil) { - src, err := NewShaderSource(s) - if err != nil { - panic(err) - } - sources = append(sources, src) - } - return sources -} - -// ShaderSource is an object encapsulating a shader source code. -type ShaderSource struct { - source []byte - id ShaderSourceID -} - -// NewShaderSource creates a new ShaderSource object from the given source code. -func NewShaderSource(source []byte) (*ShaderSource, error) { - hash, err := graphics.CalcSourceHash(source) - if err != nil { - return nil, err - } - return &ShaderSource{ - source: source, - id: ShaderSourceID(hash), - }, nil -} - -// ID returns a unique identifier for the shader source. -// The ShaderSourceID value must be the same for the same shader source and the same Ebitengine version. -// There is no guarantee that the ShaderSourceID value is the same between different Ebitengine versions. -func (s *ShaderSource) ID() ShaderSourceID { - return s.id -} - -// ShaderSourceID is a uniuqe identifier for a shader source. -type ShaderSourceID [16]byte - -// String returns a string representation of the shader source ID. -func (s ShaderSourceID) String() string { - return shaderir.SourceHash(s).String() +func AppendBuildinShaderSources(sources [][]byte) [][]byte { + return builtinshader.AppendShaderSources(sources) } diff --git a/shaderprecomp/shaderprecomp_darwin.go b/shaderprecomp/shaderprecomp_darwin.go index b795ba685..bca732836 100644 --- a/shaderprecomp/shaderprecomp_darwin.go +++ b/shaderprecomp/shaderprecomp_darwin.go @@ -21,15 +21,14 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal" - "github.com/hajimehoshi/ebiten/v2/internal/shaderir" "github.com/hajimehoshi/ebiten/v2/internal/shaderir/msl" ) // CompileToMSL compiles the shader source to Metal Shader Language, and writes the result to w. // // CompileToMSL is concurrent-safe. -func CompileToMSL(w io.Writer, source *ShaderSource) error { - ir, err := graphics.CompileShader(source.source) +func CompileToMSL(w io.Writer, kageSource []byte) error { + ir, err := graphics.CompileShader(kageSource) if err != nil { return err } @@ -44,6 +43,6 @@ func CompileToMSL(w io.Writer, source *ShaderSource) error { // For more details, see https://developer.apple.com/documentation/metal/shader_libraries/building_a_shader_library_by_precompiling_source_files. // // RegisterMetalLibrary is concurrent-safe. -func RegisterMetalLibrary(source *ShaderSource, library []byte) { - metal.RegisterPrecompiledLibrary(shaderir.SourceHash(source.ID()), library) +func RegisterMetalLibrary(kageSource []byte, library []byte) { + metal.RegisterPrecompiledLibrary(kageSource, library) } diff --git a/shaderprecomp/shaderprecomp_windows.go b/shaderprecomp/shaderprecomp_windows.go index 27c9de404..9a56b64c6 100644 --- a/shaderprecomp/shaderprecomp_windows.go +++ b/shaderprecomp/shaderprecomp_windows.go @@ -21,7 +21,6 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/directx" - "github.com/hajimehoshi/ebiten/v2/internal/shaderir" "github.com/hajimehoshi/ebiten/v2/internal/shaderir/hlsl" ) @@ -42,8 +41,8 @@ const ( // CompileToHLSL compiles the shader source to High-Level Shader Language to writers. // // CompileToHLSL is concurrent-safe. -func CompileToHLSL(vertexWriter, pixelWriter io.Writer, source *ShaderSource) error { - ir, err := graphics.CompileShader(source.source) +func CompileToHLSL(vertexWriter, pixelWriter io.Writer, kageSource []byte) error { + ir, err := graphics.CompileShader(kageSource) if err != nil { return err } @@ -62,6 +61,6 @@ func CompileToHLSL(vertexWriter, pixelWriter io.Writer, source *ShaderSource) er // For more details, see https://learn.microsoft.com/en-us/windows/win32/direct3dtools/dx-graphics-tools-fxc-using. // // RegisterFXCs is concurrent-safe. -func RegisterFXCs(source *ShaderSource, vertexFXC, pixelFXC []byte) { - directx.RegisterPrecompiledFXCs(shaderir.SourceHash(source.ID()), vertexFXC, pixelFXC) +func RegisterFXCs(kageSource []byte, vertexFXC, pixelFXC []byte) { + directx.RegisterPrecompiledFXCs(kageSource, vertexFXC, pixelFXC) } From 83ae577c80accd8c87ed896dabbcf57ee3350dfd Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 26 May 2024 22:45:12 +0900 Subject: [PATCH 69/81] Revert "shaderprecomp: remove `ShaderSource` and `ShaderSourceID`" This reverts commit 8be3bb41d5c3c432115264a07bb7fd794e478004. Reason: removing `ShaderSource` unexpected exposes the source. Updates #2999 --- examples/shaderprecomp/fxc/gen.go | 22 +++++---- examples/shaderprecomp/metallib/gen.go | 16 +++---- examples/shaderprecomp/register_darwin.go | 16 +++---- examples/shaderprecomp/register_windows.go | 18 +++----- .../graphicsdriver/directx/shader_windows.go | 4 +- .../graphicsdriver/metal/shader_darwin.go | 4 +- shaderprecomp/shaderprecomp.go | 46 ++++++++++++++++++- shaderprecomp/shaderprecomp_darwin.go | 9 ++-- shaderprecomp/shaderprecomp_windows.go | 9 ++-- 9 files changed, 91 insertions(+), 53 deletions(-) diff --git a/examples/shaderprecomp/fxc/gen.go b/examples/shaderprecomp/fxc/gen.go index b48a90695..7a6457469 100644 --- a/examples/shaderprecomp/fxc/gen.go +++ b/examples/shaderprecomp/fxc/gen.go @@ -20,10 +20,8 @@ package main import ( - "encoding/hex" "errors" "fmt" - "hash/fnv" "os" "os/exec" "path/filepath" @@ -59,7 +57,11 @@ func run() error { srcs := shaderprecomp.AppendBuildinShaderSources(nil) - defaultSrc, err := os.ReadFile(filepath.Join("..", "defaultshader.go")) + defaultSrcBytes, err := os.ReadFile(filepath.Join("..", "defaultshader.go")) + if err != nil { + return err + } + defaultSrc, err := shaderprecomp.NewShaderSource(defaultSrcBytes) if err != nil { return err } @@ -75,7 +77,9 @@ func run() error { return nil } -func generateHSLSFiles(source []byte, id string, tmpdir string) (vs, ps string, err error) { +func generateHSLSFiles(source *shaderprecomp.ShaderSource, tmpdir string) (vs, ps string, err error) { + id := source.ID().String() + vsHLSLFilePath := filepath.Join(tmpdir, id+"_vs.hlsl") vsf, err := os.Create(vsHLSLFilePath) if err != nil { @@ -97,18 +101,16 @@ func generateHSLSFiles(source []byte, id string, tmpdir string) (vs, ps string, return vsHLSLFilePath, psHLSLFilePath, nil } -func compile(kageSource []byte, tmpdir string) error { - h := fnv.New32() - _, _ = h.Write(kageSource) - id := hex.EncodeToString(h.Sum(nil)) - +func compile(source *shaderprecomp.ShaderSource, tmpdir string) error { // Generate HLSL files. Make sure this process doesn't have any handlers of the files. // Without closing the files, fxc.exe cannot access the files. - vsHLSLFilePath, psHLSLFilePath, err := generateHSLSFiles(kageSource, id, tmpdir) + vsHLSLFilePath, psHLSLFilePath, err := generateHSLSFiles(source, tmpdir) if err != nil { return err } + id := source.ID().String() + vsFXCFilePath := id + "_vs.fxc" cmd := exec.Command("fxc.exe", "/nologo", "/O3", "/T", shaderprecomp.HLSLVertexShaderProfile, "/E", shaderprecomp.HLSLVertexShaderEntryPoint, "/Fo", vsFXCFilePath, vsHLSLFilePath) cmd.Stdout = os.Stdout diff --git a/examples/shaderprecomp/metallib/gen.go b/examples/shaderprecomp/metallib/gen.go index 38027d0d2..1804c9da9 100644 --- a/examples/shaderprecomp/metallib/gen.go +++ b/examples/shaderprecomp/metallib/gen.go @@ -20,8 +20,6 @@ package main import ( - "encoding/hex" - "hash/fnv" "os" "os/exec" "path/filepath" @@ -44,7 +42,11 @@ func run() error { srcs := shaderprecomp.AppendBuildinShaderSources(nil) - defaultSrc, err := os.ReadFile(filepath.Join("..", "defaultshader.go")) + defaultSrcBytes, err := os.ReadFile(filepath.Join("..", "defaultshader.go")) + if err != nil { + return err + } + defaultSrc, err := shaderprecomp.NewShaderSource(defaultSrcBytes) if err != nil { return err } @@ -60,10 +62,8 @@ func run() error { return nil } -func compile(kageSource []byte, tmpdir string) error { - h := fnv.New32() - _, _ = h.Write(kageSource) - id := hex.EncodeToString(h.Sum(nil)) +func compile(source *shaderprecomp.ShaderSource, tmpdir string) error { + id := source.ID().String() metalFilePath := filepath.Join(tmpdir, id+".metal") @@ -73,7 +73,7 @@ func compile(kageSource []byte, tmpdir string) error { } defer f.Close() - if err := shaderprecomp.CompileToMSL(f, kageSource); err != nil { + if err := shaderprecomp.CompileToMSL(f, source); err != nil { return err } if err := f.Sync(); err != nil { diff --git a/examples/shaderprecomp/register_darwin.go b/examples/shaderprecomp/register_darwin.go index 96a63c243..b5b250cfd 100644 --- a/examples/shaderprecomp/register_darwin.go +++ b/examples/shaderprecomp/register_darwin.go @@ -16,10 +16,8 @@ package main import ( "embed" - "encoding/hex" "errors" "fmt" - "hash/fnv" "io/fs" "os" @@ -31,16 +29,14 @@ var metallibs embed.FS func registerPrecompiledShaders() error { srcs := shaderprecomp.AppendBuildinShaderSources(nil) - srcs = append(srcs, defaultShaderSourceBytes) + defaultShaderSource, err := shaderprecomp.NewShaderSource(defaultShaderSourceBytes) + if err != nil { + return err + } + srcs = append(srcs, defaultShaderSource) for _, src := range srcs { - // Calculate the hash of the source code to identify the Metal library. - // FNV is used as it is fast and the hash does not need to be secure. - h := fnv.New32() - _, _ = h.Write(src) - id := hex.EncodeToString(h.Sum(nil)) - - name := id + ".metallib" + name := src.ID().String() + ".metallib" lib, err := metallibs.ReadFile("metallib/" + name) if err != nil { if errors.Is(err, fs.ErrNotExist) { diff --git a/examples/shaderprecomp/register_windows.go b/examples/shaderprecomp/register_windows.go index 97db70188..34ac7437a 100644 --- a/examples/shaderprecomp/register_windows.go +++ b/examples/shaderprecomp/register_windows.go @@ -16,10 +16,8 @@ package main import ( "embed" - "encoding/hex" "errors" "fmt" - "hash/fnv" "io/fs" "os" @@ -33,16 +31,14 @@ var fxcs embed.FS func registerPrecompiledShaders() error { srcs := shaderprecomp.AppendBuildinShaderSources(nil) - srcs = append(srcs, defaultShaderSourceBytes) + defaultShaderSource, err := shaderprecomp.NewShaderSource(defaultShaderSourceBytes) + if err != nil { + return err + } + srcs = append(srcs, defaultShaderSource) for _, src := range srcs { - // Calculate the hash of the source code to identify the Metal library. - // FNV is used as it is fast and the hash does not need to be secure. - h := fnv.New32() - _, _ = h.Write(src) - id := hex.EncodeToString(h.Sum(nil)) - - vsname := id + "_vs.fxc" + vsname := src.ID().String() + "_vs.fxc" vs, err := fxcs.ReadFile("fxc/" + vsname) if err != nil { if errors.Is(err, fs.ErrNotExist) { @@ -52,7 +48,7 @@ func registerPrecompiledShaders() error { return err } - psname := id + "_ps.fxc" + psname := src.ID().String() + "_ps.fxc" ps, err := fxcs.ReadFile("fxc/" + psname) if err != nil { if errors.Is(err, fs.ErrNotExist) { diff --git a/internal/graphicsdriver/directx/shader_windows.go b/internal/graphicsdriver/directx/shader_windows.go index 9127ddd6e..172ec1638 100644 --- a/internal/graphicsdriver/directx/shader_windows.go +++ b/internal/graphicsdriver/directx/shader_windows.go @@ -70,8 +70,8 @@ func (c *precompiledFXCs) get(hash shaderir.SourceHash) ([]byte, []byte) { var thePrecompiledFXCs precompiledFXCs -func RegisterPrecompiledFXCs(kageSource []byte, vertex, pixel []byte) { - thePrecompiledFXCs.put(shaderir.CalcSourceHash(kageSource), vertex, pixel) +func RegisterPrecompiledFXCs(hash shaderir.SourceHash, vertex, pixel []byte) { + thePrecompiledFXCs.put(hash, vertex, pixel) } var vertexShaderCache = map[string]*_ID3DBlob{} diff --git a/internal/graphicsdriver/metal/shader_darwin.go b/internal/graphicsdriver/metal/shader_darwin.go index 64079124e..2ec9c9934 100644 --- a/internal/graphicsdriver/metal/shader_darwin.go +++ b/internal/graphicsdriver/metal/shader_darwin.go @@ -51,8 +51,8 @@ func (c *precompiledLibraries) get(hash shaderir.SourceHash) []byte { var thePrecompiledLibraries precompiledLibraries -func RegisterPrecompiledLibrary(kageSource []byte, bin []byte) { - thePrecompiledLibraries.put(shaderir.CalcSourceHash(kageSource), bin) +func RegisterPrecompiledLibrary(hash shaderir.SourceHash, bin []byte) { + thePrecompiledLibraries.put(hash, bin) } type shaderRpsKey struct { diff --git a/shaderprecomp/shaderprecomp.go b/shaderprecomp/shaderprecomp.go index d9cd6398f..650ebc1b5 100644 --- a/shaderprecomp/shaderprecomp.go +++ b/shaderprecomp/shaderprecomp.go @@ -16,6 +16,8 @@ package shaderprecomp import ( "github.com/hajimehoshi/ebiten/v2/internal/builtinshader" + "github.com/hajimehoshi/ebiten/v2/internal/graphics" + "github.com/hajimehoshi/ebiten/v2/internal/shaderir" ) // AppendBuildinShaderSources appends all the built-in shader sources to the given slice. @@ -23,6 +25,46 @@ import ( // Do not modify the content of the shader source. // // AppendBuildinShaderSources is concurrent-safe. -func AppendBuildinShaderSources(sources [][]byte) [][]byte { - return builtinshader.AppendShaderSources(sources) +func AppendBuildinShaderSources(sources []*ShaderSource) []*ShaderSource { + for _, s := range builtinshader.AppendShaderSources(nil) { + src, err := NewShaderSource(s) + if err != nil { + panic(err) + } + sources = append(sources, src) + } + return sources +} + +// ShaderSource is an object encapsulating a shader source code. +type ShaderSource struct { + source []byte + id ShaderSourceID +} + +// NewShaderSource creates a new ShaderSource object from the given source code. +func NewShaderSource(source []byte) (*ShaderSource, error) { + hash, err := graphics.CalcSourceHash(source) + if err != nil { + return nil, err + } + return &ShaderSource{ + source: source, + id: ShaderSourceID(hash), + }, nil +} + +// ID returns a unique identifier for the shader source. +// The ShaderSourceID value must be the same for the same shader source and the same Ebitengine version. +// There is no guarantee that the ShaderSourceID value is the same between different Ebitengine versions. +func (s *ShaderSource) ID() ShaderSourceID { + return s.id +} + +// ShaderSourceID is a uniuqe identifier for a shader source. +type ShaderSourceID [16]byte + +// String returns a string representation of the shader source ID. +func (s ShaderSourceID) String() string { + return shaderir.SourceHash(s).String() } diff --git a/shaderprecomp/shaderprecomp_darwin.go b/shaderprecomp/shaderprecomp_darwin.go index bca732836..b795ba685 100644 --- a/shaderprecomp/shaderprecomp_darwin.go +++ b/shaderprecomp/shaderprecomp_darwin.go @@ -21,14 +21,15 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal" + "github.com/hajimehoshi/ebiten/v2/internal/shaderir" "github.com/hajimehoshi/ebiten/v2/internal/shaderir/msl" ) // CompileToMSL compiles the shader source to Metal Shader Language, and writes the result to w. // // CompileToMSL is concurrent-safe. -func CompileToMSL(w io.Writer, kageSource []byte) error { - ir, err := graphics.CompileShader(kageSource) +func CompileToMSL(w io.Writer, source *ShaderSource) error { + ir, err := graphics.CompileShader(source.source) if err != nil { return err } @@ -43,6 +44,6 @@ func CompileToMSL(w io.Writer, kageSource []byte) error { // For more details, see https://developer.apple.com/documentation/metal/shader_libraries/building_a_shader_library_by_precompiling_source_files. // // RegisterMetalLibrary is concurrent-safe. -func RegisterMetalLibrary(kageSource []byte, library []byte) { - metal.RegisterPrecompiledLibrary(kageSource, library) +func RegisterMetalLibrary(source *ShaderSource, library []byte) { + metal.RegisterPrecompiledLibrary(shaderir.SourceHash(source.ID()), library) } diff --git a/shaderprecomp/shaderprecomp_windows.go b/shaderprecomp/shaderprecomp_windows.go index 9a56b64c6..27c9de404 100644 --- a/shaderprecomp/shaderprecomp_windows.go +++ b/shaderprecomp/shaderprecomp_windows.go @@ -21,6 +21,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/directx" + "github.com/hajimehoshi/ebiten/v2/internal/shaderir" "github.com/hajimehoshi/ebiten/v2/internal/shaderir/hlsl" ) @@ -41,8 +42,8 @@ const ( // CompileToHLSL compiles the shader source to High-Level Shader Language to writers. // // CompileToHLSL is concurrent-safe. -func CompileToHLSL(vertexWriter, pixelWriter io.Writer, kageSource []byte) error { - ir, err := graphics.CompileShader(kageSource) +func CompileToHLSL(vertexWriter, pixelWriter io.Writer, source *ShaderSource) error { + ir, err := graphics.CompileShader(source.source) if err != nil { return err } @@ -61,6 +62,6 @@ func CompileToHLSL(vertexWriter, pixelWriter io.Writer, kageSource []byte) error // For more details, see https://learn.microsoft.com/en-us/windows/win32/direct3dtools/dx-graphics-tools-fxc-using. // // RegisterFXCs is concurrent-safe. -func RegisterFXCs(kageSource []byte, vertexFXC, pixelFXC []byte) { - directx.RegisterPrecompiledFXCs(kageSource, vertexFXC, pixelFXC) +func RegisterFXCs(source *ShaderSource, vertexFXC, pixelFXC []byte) { + directx.RegisterPrecompiledFXCs(shaderir.SourceHash(source.ID()), vertexFXC, pixelFXC) } From 4818768965f1bff39e6c12ecfe76afa05d8c38b5 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 26 May 2024 22:58:12 +0900 Subject: [PATCH 70/81] shaderprecomp: remove `ShaderSourceID` `ShaderSourceID` was confusing as there was no guarantee the same ID is used for the same source if Ebitengine versions are different. `ShaderSource` should be kept as the built-in shader contents should not be exposed. Updates #2861 Closes #2999 --- examples/shaderprecomp/fxc/gen.go | 30 +++++++---------- examples/shaderprecomp/metallib/gen.go | 21 +++++------- examples/shaderprecomp/register_darwin.go | 10 ++---- examples/shaderprecomp/register_windows.go | 12 +++---- .../graphicsdriver/directx/shader_windows.go | 4 +-- .../graphicsdriver/metal/shader_darwin.go | 4 +-- .../playstation5/shader_paystation5.go | 6 +--- shaderprecomp/shaderprecomp.go | 33 ++----------------- shaderprecomp/shaderprecomp_darwin.go | 3 +- shaderprecomp/shaderprecomp_playstation5.go | 3 +- shaderprecomp/shaderprecomp_windows.go | 3 +- 11 files changed, 37 insertions(+), 92 deletions(-) diff --git a/examples/shaderprecomp/fxc/gen.go b/examples/shaderprecomp/fxc/gen.go index 7a6457469..4f7c4203f 100644 --- a/examples/shaderprecomp/fxc/gen.go +++ b/examples/shaderprecomp/fxc/gen.go @@ -40,7 +40,7 @@ func run() error { if errors.Is(err, exec.ErrNotFound) { fmt.Fprintln(os.Stderr, "fxc.exe not found. Please install Windows SDK.") fmt.Fprintln(os.Stderr, "See https://learn.microsoft.com/en-us/windows/win32/direct3dtools/fxc for more details.") - fmt.Fprintln(os.Stderr, "On PowerShell, you can add a path to the PATH environment variable temporarily like:") + fmt.Fprintln(os.Stderr, "HINT: On PowerShell, you can add a path to the PATH environment variable temporarily like:") fmt.Fprintln(os.Stderr) fmt.Fprintln(os.Stderr, ` & (Get-Process -Id $PID).Path { $env:PATH="C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64;"+$env:PATH; go generate .\examples\shaderprecomp\fxc\ }`) fmt.Fprintln(os.Stderr) @@ -61,33 +61,27 @@ func run() error { if err != nil { return err } - defaultSrc, err := shaderprecomp.NewShaderSource(defaultSrcBytes) - if err != nil { - return err - } - srcs = append(srcs, defaultSrc) + srcs = append(srcs, shaderprecomp.NewShaderSource(defaultSrcBytes)) - for _, src := range srcs { + for i, src := range srcs { // Avoid using errgroup.Group. // Compiling sources in parallel causes a mixed error message on the console. - if err := compile(src, tmpdir); err != nil { + if err := compile(src, i, tmpdir); err != nil { return err } } return nil } -func generateHSLSFiles(source *shaderprecomp.ShaderSource, tmpdir string) (vs, ps string, err error) { - id := source.ID().String() - - vsHLSLFilePath := filepath.Join(tmpdir, id+"_vs.hlsl") +func generateHSLSFiles(source *shaderprecomp.ShaderSource, index int, tmpdir string) (vs, ps string, err error) { + vsHLSLFilePath := filepath.Join(tmpdir, fmt.Sprintf("%d_vs.hlsl", index)) vsf, err := os.Create(vsHLSLFilePath) if err != nil { return "", "", err } defer vsf.Close() - psHLSLFilePath := filepath.Join(tmpdir, id+"_ps.hlsl") + psHLSLFilePath := filepath.Join(tmpdir, fmt.Sprintf("%d_ps.hlsl", index)) psf, err := os.Create(psHLSLFilePath) if err != nil { return "", "", err @@ -101,17 +95,15 @@ func generateHSLSFiles(source *shaderprecomp.ShaderSource, tmpdir string) (vs, p return vsHLSLFilePath, psHLSLFilePath, nil } -func compile(source *shaderprecomp.ShaderSource, tmpdir string) error { +func compile(source *shaderprecomp.ShaderSource, index int, tmpdir string) error { // Generate HLSL files. Make sure this process doesn't have any handlers of the files. // Without closing the files, fxc.exe cannot access the files. - vsHLSLFilePath, psHLSLFilePath, err := generateHSLSFiles(source, tmpdir) + vsHLSLFilePath, psHLSLFilePath, err := generateHSLSFiles(source, index, tmpdir) if err != nil { return err } - id := source.ID().String() - - vsFXCFilePath := id + "_vs.fxc" + vsFXCFilePath := fmt.Sprintf("%d_vs.fxc", index) cmd := exec.Command("fxc.exe", "/nologo", "/O3", "/T", shaderprecomp.HLSLVertexShaderProfile, "/E", shaderprecomp.HLSLVertexShaderEntryPoint, "/Fo", vsFXCFilePath, vsHLSLFilePath) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr @@ -119,7 +111,7 @@ func compile(source *shaderprecomp.ShaderSource, tmpdir string) error { return err } - psFXCFilePath := id + "_ps.fxc" + psFXCFilePath := fmt.Sprintf("%d_ps.fxc", index) cmd = exec.Command("fxc.exe", "/nologo", "/O3", "/T", shaderprecomp.HLSLPixelShaderProfile, "/E", shaderprecomp.HLSLPixelShaderEntryPoint, "/Fo", psFXCFilePath, psHLSLFilePath) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr diff --git a/examples/shaderprecomp/metallib/gen.go b/examples/shaderprecomp/metallib/gen.go index 1804c9da9..50264e8f2 100644 --- a/examples/shaderprecomp/metallib/gen.go +++ b/examples/shaderprecomp/metallib/gen.go @@ -20,6 +20,7 @@ package main import ( + "fmt" "os" "os/exec" "path/filepath" @@ -46,26 +47,20 @@ func run() error { if err != nil { return err } - defaultSrc, err := shaderprecomp.NewShaderSource(defaultSrcBytes) - if err != nil { - return err - } - srcs = append(srcs, defaultSrc) + srcs = append(srcs, shaderprecomp.NewShaderSource(defaultSrcBytes)) - for _, src := range srcs { + for i, src := range srcs { // Avoid using errgroup.Group. // Compiling sources in parallel causes a mixed error message on the console. - if err := compile(src, tmpdir); err != nil { + if err := compile(src, i, tmpdir); err != nil { return err } } return nil } -func compile(source *shaderprecomp.ShaderSource, tmpdir string) error { - id := source.ID().String() - - metalFilePath := filepath.Join(tmpdir, id+".metal") +func compile(source *shaderprecomp.ShaderSource, index int, tmpdir string) error { + metalFilePath := filepath.Join(tmpdir, fmt.Sprintf("%d.metal", index)) f, err := os.Create(metalFilePath) if err != nil { @@ -80,14 +75,14 @@ func compile(source *shaderprecomp.ShaderSource, tmpdir string) error { return err } - irFilePath := filepath.Join(tmpdir, id+".ir") + irFilePath := filepath.Join(tmpdir, fmt.Sprintf("%d.ir", index)) cmd := exec.Command("xcrun", "-sdk", "macosx", "metal", "-o", irFilePath, "-c", metalFilePath) cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { return err } - metallibFilePath := id + ".metallib" + metallibFilePath := fmt.Sprintf("%d.metallib", index) cmd = exec.Command("xcrun", "-sdk", "macosx", "metallib", "-o", metallibFilePath, irFilePath) cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { diff --git a/examples/shaderprecomp/register_darwin.go b/examples/shaderprecomp/register_darwin.go index b5b250cfd..c2fcb0902 100644 --- a/examples/shaderprecomp/register_darwin.go +++ b/examples/shaderprecomp/register_darwin.go @@ -29,14 +29,10 @@ var metallibs embed.FS func registerPrecompiledShaders() error { srcs := shaderprecomp.AppendBuildinShaderSources(nil) - defaultShaderSource, err := shaderprecomp.NewShaderSource(defaultShaderSourceBytes) - if err != nil { - return err - } - srcs = append(srcs, defaultShaderSource) + srcs = append(srcs, shaderprecomp.NewShaderSource(defaultShaderSourceBytes)) - for _, src := range srcs { - name := src.ID().String() + ".metallib" + for i, src := range srcs { + name := fmt.Sprintf("%d.metallib", i) lib, err := metallibs.ReadFile("metallib/" + name) if err != nil { if errors.Is(err, fs.ErrNotExist) { diff --git a/examples/shaderprecomp/register_windows.go b/examples/shaderprecomp/register_windows.go index 34ac7437a..f27b84ecf 100644 --- a/examples/shaderprecomp/register_windows.go +++ b/examples/shaderprecomp/register_windows.go @@ -31,14 +31,10 @@ var fxcs embed.FS func registerPrecompiledShaders() error { srcs := shaderprecomp.AppendBuildinShaderSources(nil) - defaultShaderSource, err := shaderprecomp.NewShaderSource(defaultShaderSourceBytes) - if err != nil { - return err - } - srcs = append(srcs, defaultShaderSource) + srcs = append(srcs, shaderprecomp.NewShaderSource(defaultShaderSourceBytes)) - for _, src := range srcs { - vsname := src.ID().String() + "_vs.fxc" + for i, src := range srcs { + vsname := fmt.Sprintf("%d_vs.fxc", i) vs, err := fxcs.ReadFile("fxc/" + vsname) if err != nil { if errors.Is(err, fs.ErrNotExist) { @@ -48,7 +44,7 @@ func registerPrecompiledShaders() error { return err } - psname := src.ID().String() + "_ps.fxc" + psname := fmt.Sprintf("%d_ps.fxc", i) ps, err := fxcs.ReadFile("fxc/" + psname) if err != nil { if errors.Is(err, fs.ErrNotExist) { diff --git a/internal/graphicsdriver/directx/shader_windows.go b/internal/graphicsdriver/directx/shader_windows.go index 172ec1638..311567659 100644 --- a/internal/graphicsdriver/directx/shader_windows.go +++ b/internal/graphicsdriver/directx/shader_windows.go @@ -70,8 +70,8 @@ func (c *precompiledFXCs) get(hash shaderir.SourceHash) ([]byte, []byte) { var thePrecompiledFXCs precompiledFXCs -func RegisterPrecompiledFXCs(hash shaderir.SourceHash, vertex, pixel []byte) { - thePrecompiledFXCs.put(hash, vertex, pixel) +func RegisterPrecompiledFXCs(source []byte, vertex, pixel []byte) { + thePrecompiledFXCs.put(shaderir.CalcSourceHash(source), vertex, pixel) } var vertexShaderCache = map[string]*_ID3DBlob{} diff --git a/internal/graphicsdriver/metal/shader_darwin.go b/internal/graphicsdriver/metal/shader_darwin.go index 2ec9c9934..dce5ebc8a 100644 --- a/internal/graphicsdriver/metal/shader_darwin.go +++ b/internal/graphicsdriver/metal/shader_darwin.go @@ -51,8 +51,8 @@ func (c *precompiledLibraries) get(hash shaderir.SourceHash) []byte { var thePrecompiledLibraries precompiledLibraries -func RegisterPrecompiledLibrary(hash shaderir.SourceHash, bin []byte) { - thePrecompiledLibraries.put(hash, bin) +func RegisterPrecompiledLibrary(source []byte, bin []byte) { + thePrecompiledLibraries.put(shaderir.CalcSourceHash(source), bin) } type shaderRpsKey struct { diff --git a/internal/graphicsdriver/playstation5/shader_paystation5.go b/internal/graphicsdriver/playstation5/shader_paystation5.go index 8128f874c..c4c9ef1e5 100644 --- a/internal/graphicsdriver/playstation5/shader_paystation5.go +++ b/internal/graphicsdriver/playstation5/shader_paystation5.go @@ -18,10 +18,6 @@ package playstation5 -import ( - "github.com/hajimehoshi/ebiten/v2/internal/shaderir" -) - -func RegisterPrecompiledShaders(hash shaderir.SourceHash, vertex, pixel []byte) { +func RegisterPrecompiledShaders(source []byte, vertex, pixel []byte) { // TODO: Implement this. } diff --git a/shaderprecomp/shaderprecomp.go b/shaderprecomp/shaderprecomp.go index 650ebc1b5..94eb0b408 100644 --- a/shaderprecomp/shaderprecomp.go +++ b/shaderprecomp/shaderprecomp.go @@ -16,8 +16,6 @@ package shaderprecomp import ( "github.com/hajimehoshi/ebiten/v2/internal/builtinshader" - "github.com/hajimehoshi/ebiten/v2/internal/graphics" - "github.com/hajimehoshi/ebiten/v2/internal/shaderir" ) // AppendBuildinShaderSources appends all the built-in shader sources to the given slice. @@ -27,11 +25,7 @@ import ( // AppendBuildinShaderSources is concurrent-safe. func AppendBuildinShaderSources(sources []*ShaderSource) []*ShaderSource { for _, s := range builtinshader.AppendShaderSources(nil) { - src, err := NewShaderSource(s) - if err != nil { - panic(err) - } - sources = append(sources, src) + sources = append(sources, NewShaderSource(s)) } return sources } @@ -39,32 +33,11 @@ func AppendBuildinShaderSources(sources []*ShaderSource) []*ShaderSource { // ShaderSource is an object encapsulating a shader source code. type ShaderSource struct { source []byte - id ShaderSourceID } // NewShaderSource creates a new ShaderSource object from the given source code. -func NewShaderSource(source []byte) (*ShaderSource, error) { - hash, err := graphics.CalcSourceHash(source) - if err != nil { - return nil, err - } +func NewShaderSource(source []byte) *ShaderSource { return &ShaderSource{ source: source, - id: ShaderSourceID(hash), - }, nil -} - -// ID returns a unique identifier for the shader source. -// The ShaderSourceID value must be the same for the same shader source and the same Ebitengine version. -// There is no guarantee that the ShaderSourceID value is the same between different Ebitengine versions. -func (s *ShaderSource) ID() ShaderSourceID { - return s.id -} - -// ShaderSourceID is a uniuqe identifier for a shader source. -type ShaderSourceID [16]byte - -// String returns a string representation of the shader source ID. -func (s ShaderSourceID) String() string { - return shaderir.SourceHash(s).String() + } } diff --git a/shaderprecomp/shaderprecomp_darwin.go b/shaderprecomp/shaderprecomp_darwin.go index b795ba685..51bb06843 100644 --- a/shaderprecomp/shaderprecomp_darwin.go +++ b/shaderprecomp/shaderprecomp_darwin.go @@ -21,7 +21,6 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal" - "github.com/hajimehoshi/ebiten/v2/internal/shaderir" "github.com/hajimehoshi/ebiten/v2/internal/shaderir/msl" ) @@ -45,5 +44,5 @@ func CompileToMSL(w io.Writer, source *ShaderSource) error { // // RegisterMetalLibrary is concurrent-safe. func RegisterMetalLibrary(source *ShaderSource, library []byte) { - metal.RegisterPrecompiledLibrary(shaderir.SourceHash(source.ID()), library) + metal.RegisterPrecompiledLibrary(source.source, library) } diff --git a/shaderprecomp/shaderprecomp_playstation5.go b/shaderprecomp/shaderprecomp_playstation5.go index 102ed5b0c..71229a048 100644 --- a/shaderprecomp/shaderprecomp_playstation5.go +++ b/shaderprecomp/shaderprecomp_playstation5.go @@ -21,7 +21,6 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/playstation5" - "github.com/hajimehoshi/ebiten/v2/internal/shaderir" "github.com/hajimehoshi/ebiten/v2/internal/shaderir/pssl" ) @@ -47,5 +46,5 @@ func CompileToPSSL(vertexWriter, pixelWriter io.Writer, source *ShaderSource) er // // RegisterPlayStationShaders is concurrent-safe. func RegisterPlayStationShaders(source *ShaderSource, vertexShader, pixelShader []byte) { - playstation5.RegisterPrecompiledShaders(shaderir.SourceHash(source.ID()), vertexShader, pixelShader) + playstation5.RegisterPrecompiledShaders(source.source, vertexShader, pixelShader) } diff --git a/shaderprecomp/shaderprecomp_windows.go b/shaderprecomp/shaderprecomp_windows.go index 27c9de404..19121e5c6 100644 --- a/shaderprecomp/shaderprecomp_windows.go +++ b/shaderprecomp/shaderprecomp_windows.go @@ -21,7 +21,6 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/directx" - "github.com/hajimehoshi/ebiten/v2/internal/shaderir" "github.com/hajimehoshi/ebiten/v2/internal/shaderir/hlsl" ) @@ -63,5 +62,5 @@ func CompileToHLSL(vertexWriter, pixelWriter io.Writer, source *ShaderSource) er // // RegisterFXCs is concurrent-safe. func RegisterFXCs(source *ShaderSource, vertexFXC, pixelFXC []byte) { - directx.RegisterPrecompiledFXCs(shaderir.SourceHash(source.ID()), vertexFXC, pixelFXC) + directx.RegisterPrecompiledFXCs(source.source, vertexFXC, pixelFXC) } From 9442b244fccfceedd3a42937a9e4179ba775b215 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 27 May 2024 23:32:17 +0900 Subject: [PATCH 71/81] internal/atlas: reduce slice allocations --- internal/atlas/image.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/internal/atlas/image.go b/internal/atlas/image.go index defce3335..c23de5147 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -256,6 +256,9 @@ type Image struct { // // usedAsDestinationCount is never reset. usedAsDestinationCount int + + // tmpBackends is a temporary slice for ensureIsolatedFromSource. + tmpBackends []*backend } // moveTo moves its content to the given image dst. @@ -436,7 +439,7 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [ return } - backends := make([]*backend, 0, len(srcs)) + i.tmpBackends = i.tmpBackends[:0] for _, src := range srcs { if src == nil { continue @@ -446,11 +449,16 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [ // If the backend becomes the same as i's, i's backend will be changed at ensureIsolatedFromSource. src.allocate(nil, true) } - backends = append(backends, src.backend) + i.tmpBackends = append(i.tmpBackends, src.backend) src.backend.sourceInThisFrame = true } - i.ensureIsolatedFromSource(backends) + i.ensureIsolatedFromSource(i.tmpBackends) + + for idx := range i.tmpBackends { + i.tmpBackends[idx] = nil + } + i.tmpBackends = i.tmpBackends[:0] for _, src := range srcs { // Compare i and source images after ensuring i is not on an atlas, or From d0aaa23005312a245f6d735964637f1aabad36b4 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Mon, 27 May 2024 23:46:48 +0900 Subject: [PATCH 72/81] Revert "internal/atlas: reduce slice allocations" This reverts commit 9442b244fccfceedd3a42937a9e4179ba775b215. Reason: the slice was not escaped to heap, so this optimization was not needed --- internal/atlas/image.go | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/internal/atlas/image.go b/internal/atlas/image.go index c23de5147..fd68311b3 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -256,9 +256,6 @@ type Image struct { // // usedAsDestinationCount is never reset. usedAsDestinationCount int - - // tmpBackends is a temporary slice for ensureIsolatedFromSource. - tmpBackends []*backend } // moveTo moves its content to the given image dst. @@ -439,7 +436,8 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [ return } - i.tmpBackends = i.tmpBackends[:0] + // This slice is not escaped to the heap. This can be checked by `go build -gcflags=-m`. + backends := make([]*backend, 0, len(srcs)) for _, src := range srcs { if src == nil { continue @@ -449,16 +447,11 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [ // If the backend becomes the same as i's, i's backend will be changed at ensureIsolatedFromSource. src.allocate(nil, true) } - i.tmpBackends = append(i.tmpBackends, src.backend) + backends = append(backends, src.backend) src.backend.sourceInThisFrame = true } - i.ensureIsolatedFromSource(i.tmpBackends) - - for idx := range i.tmpBackends { - i.tmpBackends[idx] = nil - } - i.tmpBackends = i.tmpBackends[:0] + i.ensureIsolatedFromSource(backends) for _, src := range srcs { // Compare i and source images after ensuring i is not on an atlas, or From 7ddc349ae6cd828ab01d2fe2025945b578d703d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=BA=C5=A1=20Ollah?= <57183251+MatusOllah@users.noreply.github.com> Date: Thu, 30 May 2024 17:57:32 +0200 Subject: [PATCH 73/81] text/v2: fix typo (#3004) --- text/v2/gotext.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/v2/gotext.go b/text/v2/gotext.go index 65b036180..7b42576dd 100644 --- a/text/v2/gotext.go +++ b/text/v2/gotext.go @@ -51,7 +51,7 @@ type GoTextFace struct { // Size is the font size in pixels. Size float64 - // Language is a hiint for a language (BCP 47). + // Language is a hint for a language (BCP 47). Language language.Tag // Script is a hint for a script code hint of (ISO 15924). From 216a110761116cebba4f5bbd4dae5a6c76200042 Mon Sep 17 00:00:00 2001 From: XXIV <13811862+thechampagne@users.noreply.github.com> Date: Tue, 4 Jun 2024 07:00:20 +0300 Subject: [PATCH 74/81] internal/glfw: fix memory leak (#3008) --- internal/glfw/posix_thread_darwin.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/internal/glfw/posix_thread_darwin.go b/internal/glfw/posix_thread_darwin.go index 51b4deb5e..d3228e8e0 100644 --- a/internal/glfw/posix_thread_darwin.go +++ b/internal/glfw/posix_thread_darwin.go @@ -19,8 +19,9 @@ func _glfwPlatformCreateTls(tls *C._GLFWtls) C.GLFWbool { panic("glfw: TLS must not be allocated") } if pthread_key_create(&tls.posix.key, 0) != 0 { - _glfwInputError(int32(PlatformError), - C.CString("POSIX: Failed to create context TLS")) + errstr := C.CString("POSIX: Failed to create context TLS") + defer C.free(unsafe.Pointer(errstr)) + _glfwInputError(int32(PlatformError), errstr) return False } tls.posix.allocated = True @@ -58,7 +59,9 @@ func _glfwPlatformCreateMutex(mutex *C._GLFWmutex) C.GLFWbool { panic("glfw: mutex must not be allocated") } if pthread_mutex_init(&mutex.posix.handle, nil) != 0 { - _glfwInputError(int32(PlatformError), C.CString("POSIX: Failed to create mutex")) + errstr := C.CString("POSIX: Failed to create mutex") + defer C.free(unsafe.Pointer(errstr)) + _glfwInputError(int32(PlatformError), errstr) return False } mutex.posix.allocated = True From 78ba0ded93228326714cd254a76aaaa0941ab397 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Tue, 4 Jun 2024 21:06:51 +0900 Subject: [PATCH 75/81] Revert "internal/glfw: bug fix: limit the DWM swap interval to Vista and 7" This reverts commit 86e0bcc26472c8da05fac66d84d5dd3869d55e00. Reason: This caused some issues like too much GPU usages. Updates #2961 Closes #3003 --- internal/glfw/wgl_context_windows.go | 45 +++++++++++++++++----------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/internal/glfw/wgl_context_windows.go b/internal/glfw/wgl_context_windows.go index cff168e75..305755f97 100644 --- a/internal/glfw/wgl_context_windows.go +++ b/internal/glfw/wgl_context_windows.go @@ -261,18 +261,23 @@ func makeContextCurrentWGL(window *Window) error { } func swapBuffersWGL(window *Window) error { - if window.monitor == nil { - // HACK: Use DwmFlush when desktop composition is enabled on Windows Vista and 7 - if !winver.IsWindows8OrGreater() && winver.IsWindowsVistaOrGreater() { - enabled, err := _DwmIsCompositionEnabled() + if window.monitor == nil && winver.IsWindowsVistaOrGreater() { + // DWM Composition is always enabled on Win8+ + enabled := winver.IsWindows8OrGreater() + + if !enabled { + var err error + enabled, err = _DwmIsCompositionEnabled() if err != nil { return err } - if enabled { - for i := 0; i < window.context.platform.interval; i++ { - // Ignore an error from DWM functions as they might not be implemented e.g. on Proton (#2113). - _ = _DwmFlush() - } + } + + // HACK: Use DwmFlush when desktop composition is enabled + if enabled { + for i := 0; i < window.context.platform.interval; i++ { + // Ignore an error from DWM functions as they might not be implemented e.g. on Proton (#2113). + _ = _DwmFlush() } } } @@ -292,19 +297,23 @@ func swapIntervalWGL(interval int) error { window.context.platform.interval = interval - if window.monitor == nil { - // HACK: Disable WGL swap interval when desktop composition is enabled on Windows - // Vista and 7 to avoid interfering with DWM vsync - if !winver.IsWindows8OrGreater() && winver.IsWindowsVistaOrGreater() { - enabled, err := _DwmIsCompositionEnabled() + if window.monitor == nil && winver.IsWindowsVistaOrGreater() { + // DWM Composition is always enabled on Win8+ + enabled := winver.IsWindows8OrGreater() + + if !enabled { + e, err := _DwmIsCompositionEnabled() // Ignore an error from DWM functions as they might not be implemented e.g. on Proton (#2113). if err == nil { - enabled = false - } - if enabled { - interval = 0 + enabled = e } } + + // HACK: Disable WGL swap interval when desktop composition is enabled to + // avoid interfering with DWM vsync + if enabled { + interval = 0 + } } if _glfw.platformContext.EXT_swap_control { From ab4a3af1b5fb132017ae8fdd1ab511349619af25 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 8 Jun 2024 11:54:44 +0900 Subject: [PATCH 76/81] internal/graphicsdriver: rename FillRule constants Updates #3006 --- image.go | 10 ++--- internal/atlas/image.go | 8 ++-- internal/atlas/image_test.go | 44 +++++++++---------- internal/atlas/shader_test.go | 10 ++--- internal/buffered/image.go | 2 +- internal/buffered/image_test.go | 2 +- internal/graphicscommand/command.go | 2 +- internal/graphicscommand/image_test.go | 10 ++--- internal/graphicsdriver/graphics.go | 18 ++++---- .../graphicsdriver/metal/graphics_darwin.go | 20 ++++----- internal/graphicsdriver/opengl/graphics.go | 10 ++--- internal/mipmap/mipmap.go | 2 +- internal/ui/image.go | 6 +-- 13 files changed, 72 insertions(+), 72 deletions(-) diff --git a/image.go b/image.go index 374a63e6e..18544565c 100644 --- a/image.go +++ b/image.go @@ -262,7 +262,7 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) { }) } - i.image.DrawTriangles(srcs, vs, is, blend, i.adjustedBounds(), [graphics.ShaderImageCount]image.Rectangle{img.adjustedBounds()}, shader.shader, i.tmpUniforms, graphicsdriver.FillAll, canSkipMipmap(geoM, filter), false) + i.image.DrawTriangles(srcs, vs, is, blend, i.adjustedBounds(), [graphics.ShaderImageCount]image.Rectangle{img.adjustedBounds()}, shader.shader, i.tmpUniforms, graphicsdriver.FillRuleFillAll, canSkipMipmap(geoM, filter), false) } // Vertex represents a vertex passed to DrawTriangles. @@ -312,15 +312,15 @@ type FillRule int const ( // FillAll indicates all the triangles are rendered regardless of overlaps. - FillAll FillRule = FillRule(graphicsdriver.FillAll) + FillAll FillRule = FillRule(graphicsdriver.FillRuleFillAll) // NonZero means that triangles are rendered based on the non-zero rule. // If and only if the number of overlaps is not 0, the region is rendered. - NonZero FillRule = FillRule(graphicsdriver.NonZero) + NonZero FillRule = FillRule(graphicsdriver.FillRuleNonZero) // EvenOdd means that triangles are rendered based on the even-odd rule. // If and only if the number of overlaps is odd, the region is rendered. - EvenOdd FillRule = FillRule(graphicsdriver.EvenOdd) + EvenOdd FillRule = FillRule(graphicsdriver.FillRuleEvenOdd) ) // ColorScaleMode is the mode of color scales in vertices. @@ -816,7 +816,7 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR i.tmpUniforms = i.tmpUniforms[:0] i.tmpUniforms = shader.appendUniforms(i.tmpUniforms, options.Uniforms) - i.image.DrawTriangles(imgs, vs, is, blend, i.adjustedBounds(), srcRegions, shader.shader, i.tmpUniforms, graphicsdriver.FillAll, true, false) + i.image.DrawTriangles(imgs, vs, is, blend, i.adjustedBounds(), srcRegions, shader.shader, i.tmpUniforms, graphicsdriver.FillRuleFillAll, true, false) } // SubImage returns an image representing the portion of the image p visible through r. diff --git a/internal/atlas/image.go b/internal/atlas/image.go index fd68311b3..29854e495 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -151,7 +151,7 @@ func (b *backend) extendIfNeeded(width, height int) { vs := quadVertices(0, 0, float32(sw), float32(sh), 0, 0, float32(sw), float32(sh), 1, 1, 1, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, sw, sh) - newImg.DrawTriangles(srcs, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader.ensureShader(), nil, graphicsdriver.FillAll) + newImg.DrawTriangles(srcs, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader.ensureShader(), nil, graphicsdriver.FillRuleFillAll) b.image.Dispose() b.image = newImg @@ -175,7 +175,7 @@ func newClearedImage(width, height int, screen bool) *graphicscommand.Image { func clearImage(i *graphicscommand.Image, region image.Rectangle) { vs := quadVertices(float32(region.Min.X), float32(region.Min.Y), float32(region.Max.X), float32(region.Max.Y), 0, 0, 0, 0, 0, 0, 0, 0) is := graphics.QuadIndices() - i.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendClear, region, [graphics.ShaderImageCount]image.Rectangle{}, clearShader.ensureShader(), nil, graphicsdriver.FillAll) + i.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendClear, region, [graphics.ShaderImageCount]image.Rectangle{}, clearShader.ensureShader(), nil, graphicsdriver.FillRuleFillAll) } func (b *backend) clearPixels(region image.Rectangle) { @@ -348,7 +348,7 @@ func (i *Image) ensureIsolatedFromSource(backends []*backend) { is := graphics.QuadIndices() dr := image.Rect(0, 0, i.width, i.height) - newI.drawTriangles([graphics.ShaderImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillAll) + newI.drawTriangles([graphics.ShaderImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) newI.moveTo(i) } @@ -378,7 +378,7 @@ func (i *Image) putOnSourceBackend() { graphics.QuadVertices(vs, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, i.width, i.height) - newI.drawTriangles([graphics.ShaderImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillAll) + newI.drawTriangles([graphics.ShaderImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) newI.moveTo(i) i.usedAsSourceCount = 0 diff --git a/internal/atlas/image_test.go b/internal/atlas/image_test.go index 382ceaa23..d0ba251e8 100644 --- a/internal/atlas/image_test.go +++ b/internal/atlas/image_test.go @@ -105,7 +105,7 @@ func TestEnsureIsolatedFromSourceBackend(t *testing.T) { vs := quadVertices(size/2, size/2, size/4, size/4, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, size, size) - img4.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + img4.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := img4.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -113,7 +113,7 @@ func TestEnsureIsolatedFromSourceBackend(t *testing.T) { // img5 is not allocated now, but is allocated at DrawTriangles. vs = quadVertices(0, 0, size/2, size/2, 1) dr = image.Rect(0, 0, size/2, size/2) - img3.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img5}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + img3.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img5}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := img3.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -147,7 +147,7 @@ func TestEnsureIsolatedFromSourceBackend(t *testing.T) { // Check further drawing doesn't cause panic. // This bug was fixed by 03dcd948. vs = quadVertices(0, 0, size/2, size/2, 1) - img4.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + img4.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) } func TestReputOnSourceBackend(t *testing.T) { @@ -191,7 +191,7 @@ func TestReputOnSourceBackend(t *testing.T) { // Render onto img1. The count should not matter. for i := 0; i < 5; i++ { vs := quadVertices(size, size, 0, 0, 1) - img1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + img1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -203,7 +203,7 @@ func TestReputOnSourceBackend(t *testing.T) { for i := 0; i < atlas.BaseCountToPutOnSourceBackend*2; i++ { atlas.PutImagesOnSourceBackendForTesting() vs := quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -211,7 +211,7 @@ func TestReputOnSourceBackend(t *testing.T) { // Finally, img1 is on a source backend. atlas.PutImagesOnSourceBackendForTesting() vs := quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := img1.IsOnSourceBackendForTesting(), true; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -240,7 +240,7 @@ func TestReputOnSourceBackend(t *testing.T) { } vs = quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := img1.IsOnSourceBackendForTesting(), true; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -270,7 +270,7 @@ func TestReputOnSourceBackend(t *testing.T) { // Use img1 as a render target again. The count should not matter. for i := 0; i < 5; i++ { vs := quadVertices(size, size, 0, 0, 1) - img1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + img1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -282,7 +282,7 @@ func TestReputOnSourceBackend(t *testing.T) { atlas.PutImagesOnSourceBackendForTesting() img1.WritePixels(make([]byte, 4*size*size), image.Rect(0, 0, size, size)) vs := quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -291,7 +291,7 @@ func TestReputOnSourceBackend(t *testing.T) { // img1 is not on an atlas due to WritePixels. vs = quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -300,7 +300,7 @@ func TestReputOnSourceBackend(t *testing.T) { for i := 0; i < atlas.BaseCountToPutOnSourceBackend*2; i++ { atlas.PutImagesOnSourceBackendForTesting() vs := quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := img3.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -403,7 +403,7 @@ func TestWritePixelsAfterDrawTriangles(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) dst.WritePixels(pix, image.Rect(0, 0, w, h)) pix = make([]byte, 4*w*h) @@ -450,7 +450,7 @@ func TestSmallImages(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) pix = make([]byte, 4*w*h) ok, err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, w, h)) @@ -497,7 +497,7 @@ func TestLongImages(t *testing.T) { vs := quadVertices(w, h, 0, 0, scale) is := graphics.QuadIndices() dr := image.Rect(0, 0, dstW, dstH) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) pix = make([]byte, 4*dstW*dstH) ok, err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, dstW, dstH)) @@ -613,7 +613,7 @@ func TestDeallocatedAndReputOnSourceBackend(t *testing.T) { vs := quadVertices(size, size, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, size, size) - src.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + src.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := src.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -622,7 +622,7 @@ func TestDeallocatedAndReputOnSourceBackend(t *testing.T) { for i := 0; i < atlas.BaseCountToPutOnSourceBackend/2; i++ { atlas.PutImagesOnSourceBackendForTesting() vs := quadVertices(size, size, 0, 0, 1) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := src.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -656,7 +656,7 @@ func TestImageIsNotReputOnSourceBackendWithoutUsingAsSource(t *testing.T) { // Call DrawTriangles multiple times. // The number of DrawTriangles doesn't matter as long as these are called in one frame. for i := 0; i < 2; i++ { - src2.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + src2.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) } if got, want := src2.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) @@ -675,7 +675,7 @@ func TestImageIsNotReputOnSourceBackendWithoutUsingAsSource(t *testing.T) { for i := 0; i < atlas.BaseCountToPutOnSourceBackend; i++ { atlas.PutImagesOnSourceBackendForTesting() vs := quadVertices(size, size, 0, 0, 1) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := src2.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -801,14 +801,14 @@ func TestDestinationCountOverflow(t *testing.T) { // Use dst0 as a destination for a while. for i := 0; i < 31; i++ { - dst0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + dst0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) atlas.PutImagesOnSourceBackendForTesting() } // Use dst0 as a source for a while. // As dst0 is used as a destination too many times (31 is a maximum), dst0's backend should never be a source backend. for i := 0; i < 100; i++ { - dst1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{dst0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + dst1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{dst0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) atlas.PutImagesOnSourceBackendForTesting() if dst0.IsOnSourceBackendForTesting() { t.Errorf("dst0 cannot be on a source backend: %d", i) @@ -834,7 +834,7 @@ func TestIteratingImagesToPutOnSourceBackend(t *testing.T) { is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) for _, img := range srcs { - img.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + img.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) } atlas.PutImagesOnSourceBackendForTesting() @@ -842,7 +842,7 @@ func TestIteratingImagesToPutOnSourceBackend(t *testing.T) { // Check iterating the registered image works correctly. for i := 0; i < 100; i++ { for _, src := range srcs { - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) } atlas.PutImagesOnSourceBackendForTesting() } diff --git a/internal/atlas/shader_test.go b/internal/atlas/shader_test.go index b6bcb0bfc..f022f9b7f 100644 --- a/internal/atlas/shader_test.go +++ b/internal/atlas/shader_test.go @@ -37,12 +37,12 @@ func TestShaderFillTwice(t *testing.T) { dr := image.Rect(0, 0, w, h) g := ui.Get().GraphicsDriverForTesting() s0 := atlas.NewShader(etesting.ShaderProgramFill(0xff, 0xff, 0xff, 0xff)) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, s0, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, s0, nil, graphicsdriver.FillRuleFillAll) // Vertices must be recreated (#1755) vs = quadVertices(w, h, 0, 0, 1) s1 := atlas.NewShader(etesting.ShaderProgramFill(0x80, 0x80, 0x80, 0xff)) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, s1, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, s1, nil, graphicsdriver.FillRuleFillAll) pix := make([]byte, 4*w*h) ok, err := dst.ReadPixels(g, pix, image.Rect(0, 0, w, h)) @@ -69,11 +69,11 @@ func TestImageDrawTwice(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) // Vertices must be recreated (#1755) vs = quadVertices(w, h, 0, 0, 1) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) pix := make([]byte, 4*w*h) ok, err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, w, h)) @@ -97,7 +97,7 @@ func TestGCShader(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll) // Ensure other objects are GCed, as GC appends deferred functions for collected objects. ensureGC() diff --git a/internal/buffered/image.go b/internal/buffered/image.go index 7bf2f0f96..de8eeb998 100644 --- a/internal/buffered/image.go +++ b/internal/buffered/image.go @@ -292,7 +292,7 @@ func (i *Image) syncPixelsIfNeeded() { srcs := [graphics.ShaderImageCount]*atlas.Image{whiteImage.img} dr := image.Rect(0, 0, i.width, i.height) blend := graphicsdriver.BlendCopy - i.img.DrawTriangles(srcs, vs, is, blend, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + i.img.DrawTriangles(srcs, vs, is, blend, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) // TODO: Use clear if Go 1.21 is available. for pos := range i.dotsBuffer { diff --git a/internal/buffered/image_test.go b/internal/buffered/image_test.go index 87b0dcb07..4f36bb786 100644 --- a/internal/buffered/image_test.go +++ b/internal/buffered/image_test.go @@ -56,7 +56,7 @@ func TestUnsyncedPixels(t *testing.T) { is := graphics.QuadIndices() dr := image.Rect(0, 0, 16, 16) sr := [graphics.ShaderImageCount]image.Rectangle{image.Rect(0, 0, 16, 16)} - dst.DrawTriangles([graphics.ShaderImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, sr, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, sr, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) // Check the result is correct. var got [4]byte diff --git a/internal/graphicscommand/command.go b/internal/graphicscommand/command.go index dae55d6b4..07270fcf4 100644 --- a/internal/graphicscommand/command.go +++ b/internal/graphicscommand/command.go @@ -166,7 +166,7 @@ func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs if c.fillRule != fillRule { return false } - if c.fillRule != graphicsdriver.FillAll && mightOverlapDstRegions(c.vertices, vertices) { + if c.fillRule != graphicsdriver.FillRuleFillAll && mightOverlapDstRegions(c.vertices, vertices) { return false } return true diff --git a/internal/graphicscommand/image_test.go b/internal/graphicscommand/image_test.go index 5a00e0fed..17d416301 100644 --- a/internal/graphicscommand/image_test.go +++ b/internal/graphicscommand/image_test.go @@ -59,7 +59,7 @@ func TestClear(t *testing.T) { vs := quadVertices(w/2, h/2) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{src}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{src}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll) pix := make([]byte, 4*w*h) if err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), []graphicsdriver.PixelsArgs{ @@ -90,8 +90,8 @@ func TestWritePixelsPartAfterDrawTriangles(t *testing.T) { vs := quadVertices(w/2, h/2) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{clr}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillAll) - dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{clr}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll) bs := graphics.NewManagedBytes(4, func(bs []byte) { for i := range bs { bs[i] = 0 @@ -109,11 +109,11 @@ func TestShader(t *testing.T) { vs := quadVertices(w, h) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{clr}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{clr}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll) g := ui.Get().GraphicsDriverForTesting() s := graphicscommand.NewShader(etesting.ShaderProgramFill(0xff, 0, 0, 0xff)) - dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll) pix := make([]byte, 4*w*h) if err := dst.ReadPixels(g, []graphicsdriver.PixelsArgs{ diff --git a/internal/graphicsdriver/graphics.go b/internal/graphicsdriver/graphics.go index d15d5defb..67b55d46f 100644 --- a/internal/graphicsdriver/graphics.go +++ b/internal/graphicsdriver/graphics.go @@ -30,19 +30,19 @@ type DstRegion struct { type FillRule int const ( - FillAll FillRule = iota - NonZero - EvenOdd + FillRuleFillAll FillRule = iota + FillRuleNonZero + FillRuleEvenOdd ) func (f FillRule) String() string { switch f { - case FillAll: - return "FillAll" - case NonZero: - return "NonZero" - case EvenOdd: - return "EvenOdd" + case FillRuleFillAll: + return "FillRuleFillAll" + case FillRuleNonZero: + return "FillRuleNonZero" + case FillRuleEvenOdd: + return "FillRuleEvenOdd" default: return fmt.Sprintf("FillRule(%d)", f) } diff --git a/internal/graphicsdriver/metal/graphics_darwin.go b/internal/graphicsdriver/metal/graphics_darwin.go index 1ea2bd50b..3c7659226 100644 --- a/internal/graphicsdriver/metal/graphics_darwin.go +++ b/internal/graphicsdriver/metal/graphics_darwin.go @@ -471,7 +471,7 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs // When preparing a stencil buffer, flush the current render command encoder // to make sure the stencil buffer is cleared when loading. // TODO: What about clearing the stencil buffer by vertices? - if g.lastDst != dst || g.lastFillRule != fillRule || fillRule != graphicsdriver.FillAll { + if g.lastDst != dst || g.lastFillRule != fillRule || fillRule != graphicsdriver.FillRuleFillAll { g.flushRenderCommandEncoderIfNeeded() } g.lastDst = dst @@ -497,7 +497,7 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs rpd.ColorAttachments[0].Texture = t rpd.ColorAttachments[0].ClearColor = mtl.ClearColor{} - if fillRule != graphicsdriver.FillAll { + if fillRule != graphicsdriver.FillRuleFillAll { dst.ensureStencil() rpd.StencilAttachment.LoadAction = mtl.LoadActionClear rpd.StencilAttachment.StoreAction = mtl.StoreActionDontCare @@ -544,26 +544,26 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs drawWithStencilRpss mtl.RenderPipelineState ) switch fillRule { - case graphicsdriver.FillAll: + case graphicsdriver.FillRuleFillAll: s, err := shader.RenderPipelineState(&g.view, blend, noStencil, dst.screen) if err != nil { return err } noStencilRpss = s - case graphicsdriver.NonZero: + case graphicsdriver.FillRuleNonZero: s, err := shader.RenderPipelineState(&g.view, blend, incrementStencil, dst.screen) if err != nil { return err } incrementStencilRpss = s - case graphicsdriver.EvenOdd: + case graphicsdriver.FillRuleEvenOdd: s, err := shader.RenderPipelineState(&g.view, blend, invertStencil, dst.screen) if err != nil { return err } invertStencilRpss = s } - if fillRule != graphicsdriver.FillAll { + if fillRule != graphicsdriver.FillRuleFillAll { s, err := shader.RenderPipelineState(&g.view, blend, drawWithStencil, dst.screen) if err != nil { return err @@ -580,20 +580,20 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs }) switch fillRule { - case graphicsdriver.FillAll: + case graphicsdriver.FillRuleFillAll: g.rce.SetDepthStencilState(g.dsss[noStencil]) g.rce.SetRenderPipelineState(noStencilRpss) g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0)))) - case graphicsdriver.NonZero: + case graphicsdriver.FillRuleNonZero: g.rce.SetDepthStencilState(g.dsss[incrementStencil]) g.rce.SetRenderPipelineState(incrementStencilRpss) g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0)))) - case graphicsdriver.EvenOdd: + case graphicsdriver.FillRuleEvenOdd: g.rce.SetDepthStencilState(g.dsss[invertStencil]) g.rce.SetRenderPipelineState(invertStencilRpss) g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0)))) } - if fillRule != graphicsdriver.FillAll { + if fillRule != graphicsdriver.FillRuleFillAll { g.rce.SetDepthStencilState(g.dsss[drawWithStencil]) g.rce.SetRenderPipelineState(drawWithStencilRpss) g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0)))) diff --git a/internal/graphicsdriver/opengl/graphics.go b/internal/graphicsdriver/opengl/graphics.go index 44b20d620..6f1cd44de 100644 --- a/internal/graphicsdriver/opengl/graphics.go +++ b/internal/graphicsdriver/opengl/graphics.go @@ -259,7 +259,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics. } g.uniformVars = g.uniformVars[:0] - if fillRule != graphicsdriver.FillAll { + if fillRule != graphicsdriver.FillRuleFillAll { if err := destination.ensureStencilBuffer(); err != nil { return err } @@ -274,14 +274,14 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics. int32(dstRegion.Region.Dy()), ) switch fillRule { - case graphicsdriver.NonZero: + case graphicsdriver.FillRuleNonZero: g.context.ctx.Clear(gl.STENCIL_BUFFER_BIT) g.context.ctx.StencilFunc(gl.ALWAYS, 0x00, 0xff) g.context.ctx.StencilOpSeparate(gl.FRONT, gl.KEEP, gl.KEEP, gl.INCR_WRAP) g.context.ctx.StencilOpSeparate(gl.BACK, gl.KEEP, gl.KEEP, gl.DECR_WRAP) g.context.ctx.ColorMask(false, false, false, false) g.context.ctx.DrawElements(gl.TRIANGLES, int32(dstRegion.IndexCount), gl.UNSIGNED_INT, indexOffset*int(unsafe.Sizeof(uint32(0)))) - case graphicsdriver.EvenOdd: + case graphicsdriver.FillRuleEvenOdd: g.context.ctx.Clear(gl.STENCIL_BUFFER_BIT) g.context.ctx.StencilFunc(gl.ALWAYS, 0x00, 0xff) g.context.ctx.StencilOpSeparate(gl.FRONT_AND_BACK, gl.KEEP, gl.KEEP, gl.INVERT) @@ -289,7 +289,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics. g.context.ctx.DrawElements(gl.TRIANGLES, int32(dstRegion.IndexCount), gl.UNSIGNED_INT, indexOffset*int(unsafe.Sizeof(uint32(0)))) } - if fillRule != graphicsdriver.FillAll { + if fillRule != graphicsdriver.FillRuleFillAll { g.context.ctx.StencilFunc(gl.NOTEQUAL, 0x00, 0xff) g.context.ctx.StencilOpSeparate(gl.FRONT_AND_BACK, gl.KEEP, gl.KEEP, gl.KEEP) g.context.ctx.ColorMask(true, true, true, true) @@ -298,7 +298,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics. indexOffset += dstRegion.IndexCount } - if fillRule != graphicsdriver.FillAll { + if fillRule != graphicsdriver.FillRuleFillAll { g.context.ctx.Disable(gl.STENCIL_TEST) } diff --git a/internal/mipmap/mipmap.go b/internal/mipmap/mipmap.go index 47ca0cad7..6476ab2a0 100644 --- a/internal/mipmap/mipmap.go +++ b/internal/mipmap/mipmap.go @@ -187,7 +187,7 @@ func (m *Mipmap) level(level int) *buffered.Image { s := buffered.NewImage(w2, h2, m.imageType) dstRegion := image.Rect(0, 0, w2, h2) - s.DrawTriangles([graphics.ShaderImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderImageCount]image.Rectangle{}, shader, nil, graphicsdriver.FillAll) + s.DrawTriangles([graphics.ShaderImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderImageCount]image.Rectangle{}, shader, nil, graphicsdriver.FillRuleFillAll) m.setImg(level, s) return m.imgs[level] diff --git a/internal/ui/image.go b/internal/ui/image.go index 093bfb654..31db743ab 100644 --- a/internal/ui/image.go +++ b/internal/ui/image.go @@ -183,7 +183,7 @@ func (i *Image) Fill(r, g, b, a float32, region image.Rectangle) { blend = graphicsdriver.BlendSourceOver } // i.lastBlend is updated in DrawTriangles. - i.DrawTriangles(srcs, i.tmpVerticesForFill, is, blend, region, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillAll, true, false) + i.DrawTriangles(srcs, i.tmpVerticesForFill, is, blend, region, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false) } type bigOffscreenImage struct { @@ -252,7 +252,7 @@ func (i *bigOffscreenImage) drawTriangles(srcs [graphics.ShaderImageCount]*Image 1, 1, 1, 1) is := graphics.QuadIndices() dstRegion := image.Rect(0, 0, i.region.Dx()*bigOffscreenScale, i.region.Dy()*bigOffscreenScale) - i.image.DrawTriangles(srcs, i.tmpVerticesForCopying, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillAll, true, false) + i.image.DrawTriangles(srcs, i.tmpVerticesForCopying, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false) } for idx := 0; idx < len(vertices); idx += graphics.VertexFloatCount { @@ -300,7 +300,7 @@ func (i *bigOffscreenImage) flush() { if i.blend != graphicsdriver.BlendSourceOver { blend = graphicsdriver.BlendCopy } - i.orig.DrawTriangles(srcs, i.tmpVerticesForFlushing, is, blend, dstRegion, [graphics.ShaderImageCount]image.Rectangle{}, LinearFilterShader, nil, graphicsdriver.FillAll, true, false) + i.orig.DrawTriangles(srcs, i.tmpVerticesForFlushing, is, blend, dstRegion, [graphics.ShaderImageCount]image.Rectangle{}, LinearFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false) i.image.clear() i.dirty = false From 6ac1270cb081b473556fff0a484e5464cfa5e9fe Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 8 Jun 2024 12:10:27 +0900 Subject: [PATCH 77/81] Revert "internal/graphicsdriver: rename FillRule constants" This reverts commit ab4a3af1b5fb132017ae8fdd1ab511349619af25. Reason: compile error on Windows --- image.go | 10 ++--- internal/atlas/image.go | 8 ++-- internal/atlas/image_test.go | 44 +++++++++---------- internal/atlas/shader_test.go | 10 ++--- internal/buffered/image.go | 2 +- internal/buffered/image_test.go | 2 +- internal/graphicscommand/command.go | 2 +- internal/graphicscommand/image_test.go | 10 ++--- internal/graphicsdriver/graphics.go | 18 ++++---- .../graphicsdriver/metal/graphics_darwin.go | 20 ++++----- internal/graphicsdriver/opengl/graphics.go | 10 ++--- internal/mipmap/mipmap.go | 2 +- internal/ui/image.go | 6 +-- 13 files changed, 72 insertions(+), 72 deletions(-) diff --git a/image.go b/image.go index 18544565c..374a63e6e 100644 --- a/image.go +++ b/image.go @@ -262,7 +262,7 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) { }) } - i.image.DrawTriangles(srcs, vs, is, blend, i.adjustedBounds(), [graphics.ShaderImageCount]image.Rectangle{img.adjustedBounds()}, shader.shader, i.tmpUniforms, graphicsdriver.FillRuleFillAll, canSkipMipmap(geoM, filter), false) + i.image.DrawTriangles(srcs, vs, is, blend, i.adjustedBounds(), [graphics.ShaderImageCount]image.Rectangle{img.adjustedBounds()}, shader.shader, i.tmpUniforms, graphicsdriver.FillAll, canSkipMipmap(geoM, filter), false) } // Vertex represents a vertex passed to DrawTriangles. @@ -312,15 +312,15 @@ type FillRule int const ( // FillAll indicates all the triangles are rendered regardless of overlaps. - FillAll FillRule = FillRule(graphicsdriver.FillRuleFillAll) + FillAll FillRule = FillRule(graphicsdriver.FillAll) // NonZero means that triangles are rendered based on the non-zero rule. // If and only if the number of overlaps is not 0, the region is rendered. - NonZero FillRule = FillRule(graphicsdriver.FillRuleNonZero) + NonZero FillRule = FillRule(graphicsdriver.NonZero) // EvenOdd means that triangles are rendered based on the even-odd rule. // If and only if the number of overlaps is odd, the region is rendered. - EvenOdd FillRule = FillRule(graphicsdriver.FillRuleEvenOdd) + EvenOdd FillRule = FillRule(graphicsdriver.EvenOdd) ) // ColorScaleMode is the mode of color scales in vertices. @@ -816,7 +816,7 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR i.tmpUniforms = i.tmpUniforms[:0] i.tmpUniforms = shader.appendUniforms(i.tmpUniforms, options.Uniforms) - i.image.DrawTriangles(imgs, vs, is, blend, i.adjustedBounds(), srcRegions, shader.shader, i.tmpUniforms, graphicsdriver.FillRuleFillAll, true, false) + i.image.DrawTriangles(imgs, vs, is, blend, i.adjustedBounds(), srcRegions, shader.shader, i.tmpUniforms, graphicsdriver.FillAll, true, false) } // SubImage returns an image representing the portion of the image p visible through r. diff --git a/internal/atlas/image.go b/internal/atlas/image.go index 29854e495..fd68311b3 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -151,7 +151,7 @@ func (b *backend) extendIfNeeded(width, height int) { vs := quadVertices(0, 0, float32(sw), float32(sh), 0, 0, float32(sw), float32(sh), 1, 1, 1, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, sw, sh) - newImg.DrawTriangles(srcs, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader.ensureShader(), nil, graphicsdriver.FillRuleFillAll) + newImg.DrawTriangles(srcs, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader.ensureShader(), nil, graphicsdriver.FillAll) b.image.Dispose() b.image = newImg @@ -175,7 +175,7 @@ func newClearedImage(width, height int, screen bool) *graphicscommand.Image { func clearImage(i *graphicscommand.Image, region image.Rectangle) { vs := quadVertices(float32(region.Min.X), float32(region.Min.Y), float32(region.Max.X), float32(region.Max.Y), 0, 0, 0, 0, 0, 0, 0, 0) is := graphics.QuadIndices() - i.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendClear, region, [graphics.ShaderImageCount]image.Rectangle{}, clearShader.ensureShader(), nil, graphicsdriver.FillRuleFillAll) + i.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendClear, region, [graphics.ShaderImageCount]image.Rectangle{}, clearShader.ensureShader(), nil, graphicsdriver.FillAll) } func (b *backend) clearPixels(region image.Rectangle) { @@ -348,7 +348,7 @@ func (i *Image) ensureIsolatedFromSource(backends []*backend) { is := graphics.QuadIndices() dr := image.Rect(0, 0, i.width, i.height) - newI.drawTriangles([graphics.ShaderImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + newI.drawTriangles([graphics.ShaderImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillAll) newI.moveTo(i) } @@ -378,7 +378,7 @@ func (i *Image) putOnSourceBackend() { graphics.QuadVertices(vs, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, i.width, i.height) - newI.drawTriangles([graphics.ShaderImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + newI.drawTriangles([graphics.ShaderImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillAll) newI.moveTo(i) i.usedAsSourceCount = 0 diff --git a/internal/atlas/image_test.go b/internal/atlas/image_test.go index d0ba251e8..382ceaa23 100644 --- a/internal/atlas/image_test.go +++ b/internal/atlas/image_test.go @@ -105,7 +105,7 @@ func TestEnsureIsolatedFromSourceBackend(t *testing.T) { vs := quadVertices(size/2, size/2, size/4, size/4, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, size, size) - img4.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img4.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) if got, want := img4.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -113,7 +113,7 @@ func TestEnsureIsolatedFromSourceBackend(t *testing.T) { // img5 is not allocated now, but is allocated at DrawTriangles. vs = quadVertices(0, 0, size/2, size/2, 1) dr = image.Rect(0, 0, size/2, size/2) - img3.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img5}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img3.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img5}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) if got, want := img3.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -147,7 +147,7 @@ func TestEnsureIsolatedFromSourceBackend(t *testing.T) { // Check further drawing doesn't cause panic. // This bug was fixed by 03dcd948. vs = quadVertices(0, 0, size/2, size/2, 1) - img4.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img4.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) } func TestReputOnSourceBackend(t *testing.T) { @@ -191,7 +191,7 @@ func TestReputOnSourceBackend(t *testing.T) { // Render onto img1. The count should not matter. for i := 0; i < 5; i++ { vs := quadVertices(size, size, 0, 0, 1) - img1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -203,7 +203,7 @@ func TestReputOnSourceBackend(t *testing.T) { for i := 0; i < atlas.BaseCountToPutOnSourceBackend*2; i++ { atlas.PutImagesOnSourceBackendForTesting() vs := quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -211,7 +211,7 @@ func TestReputOnSourceBackend(t *testing.T) { // Finally, img1 is on a source backend. atlas.PutImagesOnSourceBackendForTesting() vs := quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) if got, want := img1.IsOnSourceBackendForTesting(), true; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -240,7 +240,7 @@ func TestReputOnSourceBackend(t *testing.T) { } vs = quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) if got, want := img1.IsOnSourceBackendForTesting(), true; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -270,7 +270,7 @@ func TestReputOnSourceBackend(t *testing.T) { // Use img1 as a render target again. The count should not matter. for i := 0; i < 5; i++ { vs := quadVertices(size, size, 0, 0, 1) - img1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -282,7 +282,7 @@ func TestReputOnSourceBackend(t *testing.T) { atlas.PutImagesOnSourceBackendForTesting() img1.WritePixels(make([]byte, 4*size*size), image.Rect(0, 0, size, size)) vs := quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -291,7 +291,7 @@ func TestReputOnSourceBackend(t *testing.T) { // img1 is not on an atlas due to WritePixels. vs = quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -300,7 +300,7 @@ func TestReputOnSourceBackend(t *testing.T) { for i := 0; i < atlas.BaseCountToPutOnSourceBackend*2; i++ { atlas.PutImagesOnSourceBackendForTesting() vs := quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) if got, want := img3.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -403,7 +403,7 @@ func TestWritePixelsAfterDrawTriangles(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) dst.WritePixels(pix, image.Rect(0, 0, w, h)) pix = make([]byte, 4*w*h) @@ -450,7 +450,7 @@ func TestSmallImages(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) pix = make([]byte, 4*w*h) ok, err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, w, h)) @@ -497,7 +497,7 @@ func TestLongImages(t *testing.T) { vs := quadVertices(w, h, 0, 0, scale) is := graphics.QuadIndices() dr := image.Rect(0, 0, dstW, dstH) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) pix = make([]byte, 4*dstW*dstH) ok, err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, dstW, dstH)) @@ -613,7 +613,7 @@ func TestDeallocatedAndReputOnSourceBackend(t *testing.T) { vs := quadVertices(size, size, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, size, size) - src.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + src.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) if got, want := src.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -622,7 +622,7 @@ func TestDeallocatedAndReputOnSourceBackend(t *testing.T) { for i := 0; i < atlas.BaseCountToPutOnSourceBackend/2; i++ { atlas.PutImagesOnSourceBackendForTesting() vs := quadVertices(size, size, 0, 0, 1) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) if got, want := src.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -656,7 +656,7 @@ func TestImageIsNotReputOnSourceBackendWithoutUsingAsSource(t *testing.T) { // Call DrawTriangles multiple times. // The number of DrawTriangles doesn't matter as long as these are called in one frame. for i := 0; i < 2; i++ { - src2.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + src2.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) } if got, want := src2.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) @@ -675,7 +675,7 @@ func TestImageIsNotReputOnSourceBackendWithoutUsingAsSource(t *testing.T) { for i := 0; i < atlas.BaseCountToPutOnSourceBackend; i++ { atlas.PutImagesOnSourceBackendForTesting() vs := quadVertices(size, size, 0, 0, 1) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) if got, want := src2.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -801,14 +801,14 @@ func TestDestinationCountOverflow(t *testing.T) { // Use dst0 as a destination for a while. for i := 0; i < 31; i++ { - dst0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) atlas.PutImagesOnSourceBackendForTesting() } // Use dst0 as a source for a while. // As dst0 is used as a destination too many times (31 is a maximum), dst0's backend should never be a source backend. for i := 0; i < 100; i++ { - dst1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{dst0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{dst0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) atlas.PutImagesOnSourceBackendForTesting() if dst0.IsOnSourceBackendForTesting() { t.Errorf("dst0 cannot be on a source backend: %d", i) @@ -834,7 +834,7 @@ func TestIteratingImagesToPutOnSourceBackend(t *testing.T) { is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) for _, img := range srcs { - img.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + img.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) } atlas.PutImagesOnSourceBackendForTesting() @@ -842,7 +842,7 @@ func TestIteratingImagesToPutOnSourceBackend(t *testing.T) { // Check iterating the registered image works correctly. for i := 0; i < 100; i++ { for _, src := range srcs { - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) } atlas.PutImagesOnSourceBackendForTesting() } diff --git a/internal/atlas/shader_test.go b/internal/atlas/shader_test.go index f022f9b7f..b6bcb0bfc 100644 --- a/internal/atlas/shader_test.go +++ b/internal/atlas/shader_test.go @@ -37,12 +37,12 @@ func TestShaderFillTwice(t *testing.T) { dr := image.Rect(0, 0, w, h) g := ui.Get().GraphicsDriverForTesting() s0 := atlas.NewShader(etesting.ShaderProgramFill(0xff, 0xff, 0xff, 0xff)) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, s0, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, s0, nil, graphicsdriver.FillAll) // Vertices must be recreated (#1755) vs = quadVertices(w, h, 0, 0, 1) s1 := atlas.NewShader(etesting.ShaderProgramFill(0x80, 0x80, 0x80, 0xff)) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, s1, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, s1, nil, graphicsdriver.FillAll) pix := make([]byte, 4*w*h) ok, err := dst.ReadPixels(g, pix, image.Rect(0, 0, w, h)) @@ -69,11 +69,11 @@ func TestImageDrawTwice(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) // Vertices must be recreated (#1755) vs = quadVertices(w, h, 0, 0, 1) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) pix := make([]byte, 4*w*h) ok, err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, w, h)) @@ -97,7 +97,7 @@ func TestGCShader(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillAll) // Ensure other objects are GCed, as GC appends deferred functions for collected objects. ensureGC() diff --git a/internal/buffered/image.go b/internal/buffered/image.go index de8eeb998..7bf2f0f96 100644 --- a/internal/buffered/image.go +++ b/internal/buffered/image.go @@ -292,7 +292,7 @@ func (i *Image) syncPixelsIfNeeded() { srcs := [graphics.ShaderImageCount]*atlas.Image{whiteImage.img} dr := image.Rect(0, 0, i.width, i.height) blend := graphicsdriver.BlendCopy - i.img.DrawTriangles(srcs, vs, is, blend, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + i.img.DrawTriangles(srcs, vs, is, blend, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) // TODO: Use clear if Go 1.21 is available. for pos := range i.dotsBuffer { diff --git a/internal/buffered/image_test.go b/internal/buffered/image_test.go index 4f36bb786..87b0dcb07 100644 --- a/internal/buffered/image_test.go +++ b/internal/buffered/image_test.go @@ -56,7 +56,7 @@ func TestUnsyncedPixels(t *testing.T) { is := graphics.QuadIndices() dr := image.Rect(0, 0, 16, 16) sr := [graphics.ShaderImageCount]image.Rectangle{image.Rect(0, 0, 16, 16)} - dst.DrawTriangles([graphics.ShaderImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, sr, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, sr, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) // Check the result is correct. var got [4]byte diff --git a/internal/graphicscommand/command.go b/internal/graphicscommand/command.go index 07270fcf4..dae55d6b4 100644 --- a/internal/graphicscommand/command.go +++ b/internal/graphicscommand/command.go @@ -166,7 +166,7 @@ func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs if c.fillRule != fillRule { return false } - if c.fillRule != graphicsdriver.FillRuleFillAll && mightOverlapDstRegions(c.vertices, vertices) { + if c.fillRule != graphicsdriver.FillAll && mightOverlapDstRegions(c.vertices, vertices) { return false } return true diff --git a/internal/graphicscommand/image_test.go b/internal/graphicscommand/image_test.go index 17d416301..5a00e0fed 100644 --- a/internal/graphicscommand/image_test.go +++ b/internal/graphicscommand/image_test.go @@ -59,7 +59,7 @@ func TestClear(t *testing.T) { vs := quadVertices(w/2, h/2) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{src}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{src}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillAll) pix := make([]byte, 4*w*h) if err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), []graphicsdriver.PixelsArgs{ @@ -90,8 +90,8 @@ func TestWritePixelsPartAfterDrawTriangles(t *testing.T) { vs := quadVertices(w/2, h/2) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{clr}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll) - dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{clr}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillAll) bs := graphics.NewManagedBytes(4, func(bs []byte) { for i := range bs { bs[i] = 0 @@ -109,11 +109,11 @@ func TestShader(t *testing.T) { vs := quadVertices(w, h) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{clr}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{clr}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillAll) g := ui.Get().GraphicsDriverForTesting() s := graphicscommand.NewShader(etesting.ShaderProgramFill(0xff, 0, 0, 0xff)) - dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillAll) pix := make([]byte, 4*w*h) if err := dst.ReadPixels(g, []graphicsdriver.PixelsArgs{ diff --git a/internal/graphicsdriver/graphics.go b/internal/graphicsdriver/graphics.go index 67b55d46f..d15d5defb 100644 --- a/internal/graphicsdriver/graphics.go +++ b/internal/graphicsdriver/graphics.go @@ -30,19 +30,19 @@ type DstRegion struct { type FillRule int const ( - FillRuleFillAll FillRule = iota - FillRuleNonZero - FillRuleEvenOdd + FillAll FillRule = iota + NonZero + EvenOdd ) func (f FillRule) String() string { switch f { - case FillRuleFillAll: - return "FillRuleFillAll" - case FillRuleNonZero: - return "FillRuleNonZero" - case FillRuleEvenOdd: - return "FillRuleEvenOdd" + case FillAll: + return "FillAll" + case NonZero: + return "NonZero" + case EvenOdd: + return "EvenOdd" default: return fmt.Sprintf("FillRule(%d)", f) } diff --git a/internal/graphicsdriver/metal/graphics_darwin.go b/internal/graphicsdriver/metal/graphics_darwin.go index 3c7659226..1ea2bd50b 100644 --- a/internal/graphicsdriver/metal/graphics_darwin.go +++ b/internal/graphicsdriver/metal/graphics_darwin.go @@ -471,7 +471,7 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs // When preparing a stencil buffer, flush the current render command encoder // to make sure the stencil buffer is cleared when loading. // TODO: What about clearing the stencil buffer by vertices? - if g.lastDst != dst || g.lastFillRule != fillRule || fillRule != graphicsdriver.FillRuleFillAll { + if g.lastDst != dst || g.lastFillRule != fillRule || fillRule != graphicsdriver.FillAll { g.flushRenderCommandEncoderIfNeeded() } g.lastDst = dst @@ -497,7 +497,7 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs rpd.ColorAttachments[0].Texture = t rpd.ColorAttachments[0].ClearColor = mtl.ClearColor{} - if fillRule != graphicsdriver.FillRuleFillAll { + if fillRule != graphicsdriver.FillAll { dst.ensureStencil() rpd.StencilAttachment.LoadAction = mtl.LoadActionClear rpd.StencilAttachment.StoreAction = mtl.StoreActionDontCare @@ -544,26 +544,26 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs drawWithStencilRpss mtl.RenderPipelineState ) switch fillRule { - case graphicsdriver.FillRuleFillAll: + case graphicsdriver.FillAll: s, err := shader.RenderPipelineState(&g.view, blend, noStencil, dst.screen) if err != nil { return err } noStencilRpss = s - case graphicsdriver.FillRuleNonZero: + case graphicsdriver.NonZero: s, err := shader.RenderPipelineState(&g.view, blend, incrementStencil, dst.screen) if err != nil { return err } incrementStencilRpss = s - case graphicsdriver.FillRuleEvenOdd: + case graphicsdriver.EvenOdd: s, err := shader.RenderPipelineState(&g.view, blend, invertStencil, dst.screen) if err != nil { return err } invertStencilRpss = s } - if fillRule != graphicsdriver.FillRuleFillAll { + if fillRule != graphicsdriver.FillAll { s, err := shader.RenderPipelineState(&g.view, blend, drawWithStencil, dst.screen) if err != nil { return err @@ -580,20 +580,20 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs }) switch fillRule { - case graphicsdriver.FillRuleFillAll: + case graphicsdriver.FillAll: g.rce.SetDepthStencilState(g.dsss[noStencil]) g.rce.SetRenderPipelineState(noStencilRpss) g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0)))) - case graphicsdriver.FillRuleNonZero: + case graphicsdriver.NonZero: g.rce.SetDepthStencilState(g.dsss[incrementStencil]) g.rce.SetRenderPipelineState(incrementStencilRpss) g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0)))) - case graphicsdriver.FillRuleEvenOdd: + case graphicsdriver.EvenOdd: g.rce.SetDepthStencilState(g.dsss[invertStencil]) g.rce.SetRenderPipelineState(invertStencilRpss) g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0)))) } - if fillRule != graphicsdriver.FillRuleFillAll { + if fillRule != graphicsdriver.FillAll { g.rce.SetDepthStencilState(g.dsss[drawWithStencil]) g.rce.SetRenderPipelineState(drawWithStencilRpss) g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0)))) diff --git a/internal/graphicsdriver/opengl/graphics.go b/internal/graphicsdriver/opengl/graphics.go index 6f1cd44de..44b20d620 100644 --- a/internal/graphicsdriver/opengl/graphics.go +++ b/internal/graphicsdriver/opengl/graphics.go @@ -259,7 +259,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics. } g.uniformVars = g.uniformVars[:0] - if fillRule != graphicsdriver.FillRuleFillAll { + if fillRule != graphicsdriver.FillAll { if err := destination.ensureStencilBuffer(); err != nil { return err } @@ -274,14 +274,14 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics. int32(dstRegion.Region.Dy()), ) switch fillRule { - case graphicsdriver.FillRuleNonZero: + case graphicsdriver.NonZero: g.context.ctx.Clear(gl.STENCIL_BUFFER_BIT) g.context.ctx.StencilFunc(gl.ALWAYS, 0x00, 0xff) g.context.ctx.StencilOpSeparate(gl.FRONT, gl.KEEP, gl.KEEP, gl.INCR_WRAP) g.context.ctx.StencilOpSeparate(gl.BACK, gl.KEEP, gl.KEEP, gl.DECR_WRAP) g.context.ctx.ColorMask(false, false, false, false) g.context.ctx.DrawElements(gl.TRIANGLES, int32(dstRegion.IndexCount), gl.UNSIGNED_INT, indexOffset*int(unsafe.Sizeof(uint32(0)))) - case graphicsdriver.FillRuleEvenOdd: + case graphicsdriver.EvenOdd: g.context.ctx.Clear(gl.STENCIL_BUFFER_BIT) g.context.ctx.StencilFunc(gl.ALWAYS, 0x00, 0xff) g.context.ctx.StencilOpSeparate(gl.FRONT_AND_BACK, gl.KEEP, gl.KEEP, gl.INVERT) @@ -289,7 +289,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics. g.context.ctx.DrawElements(gl.TRIANGLES, int32(dstRegion.IndexCount), gl.UNSIGNED_INT, indexOffset*int(unsafe.Sizeof(uint32(0)))) } - if fillRule != graphicsdriver.FillRuleFillAll { + if fillRule != graphicsdriver.FillAll { g.context.ctx.StencilFunc(gl.NOTEQUAL, 0x00, 0xff) g.context.ctx.StencilOpSeparate(gl.FRONT_AND_BACK, gl.KEEP, gl.KEEP, gl.KEEP) g.context.ctx.ColorMask(true, true, true, true) @@ -298,7 +298,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics. indexOffset += dstRegion.IndexCount } - if fillRule != graphicsdriver.FillRuleFillAll { + if fillRule != graphicsdriver.FillAll { g.context.ctx.Disable(gl.STENCIL_TEST) } diff --git a/internal/mipmap/mipmap.go b/internal/mipmap/mipmap.go index 6476ab2a0..47ca0cad7 100644 --- a/internal/mipmap/mipmap.go +++ b/internal/mipmap/mipmap.go @@ -187,7 +187,7 @@ func (m *Mipmap) level(level int) *buffered.Image { s := buffered.NewImage(w2, h2, m.imageType) dstRegion := image.Rect(0, 0, w2, h2) - s.DrawTriangles([graphics.ShaderImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderImageCount]image.Rectangle{}, shader, nil, graphicsdriver.FillRuleFillAll) + s.DrawTriangles([graphics.ShaderImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderImageCount]image.Rectangle{}, shader, nil, graphicsdriver.FillAll) m.setImg(level, s) return m.imgs[level] diff --git a/internal/ui/image.go b/internal/ui/image.go index 31db743ab..093bfb654 100644 --- a/internal/ui/image.go +++ b/internal/ui/image.go @@ -183,7 +183,7 @@ func (i *Image) Fill(r, g, b, a float32, region image.Rectangle) { blend = graphicsdriver.BlendSourceOver } // i.lastBlend is updated in DrawTriangles. - i.DrawTriangles(srcs, i.tmpVerticesForFill, is, blend, region, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false) + i.DrawTriangles(srcs, i.tmpVerticesForFill, is, blend, region, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillAll, true, false) } type bigOffscreenImage struct { @@ -252,7 +252,7 @@ func (i *bigOffscreenImage) drawTriangles(srcs [graphics.ShaderImageCount]*Image 1, 1, 1, 1) is := graphics.QuadIndices() dstRegion := image.Rect(0, 0, i.region.Dx()*bigOffscreenScale, i.region.Dy()*bigOffscreenScale) - i.image.DrawTriangles(srcs, i.tmpVerticesForCopying, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false) + i.image.DrawTriangles(srcs, i.tmpVerticesForCopying, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillAll, true, false) } for idx := 0; idx < len(vertices); idx += graphics.VertexFloatCount { @@ -300,7 +300,7 @@ func (i *bigOffscreenImage) flush() { if i.blend != graphicsdriver.BlendSourceOver { blend = graphicsdriver.BlendCopy } - i.orig.DrawTriangles(srcs, i.tmpVerticesForFlushing, is, blend, dstRegion, [graphics.ShaderImageCount]image.Rectangle{}, LinearFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false) + i.orig.DrawTriangles(srcs, i.tmpVerticesForFlushing, is, blend, dstRegion, [graphics.ShaderImageCount]image.Rectangle{}, LinearFilterShader, nil, graphicsdriver.FillAll, true, false) i.image.clear() i.dirty = false From e5d10c47e73c4083a11e68d6ca06d39e5e4643a1 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 8 Jun 2024 11:54:44 +0900 Subject: [PATCH 78/81] internal/graphicsdriver: reland: rename FillRule constants Updates #3006 --- image.go | 10 ++--- internal/atlas/image.go | 8 ++-- internal/atlas/image_test.go | 44 +++++++++---------- internal/atlas/shader_test.go | 10 ++--- internal/buffered/image.go | 2 +- internal/buffered/image_test.go | 2 +- internal/graphicscommand/command.go | 2 +- internal/graphicscommand/image_test.go | 10 ++--- .../directx/graphics11_windows.go | 12 ++--- .../directx/graphics12_windows.go | 4 +- .../directx/pipeline12_windows.go | 10 ++--- internal/graphicsdriver/graphics.go | 18 ++++---- .../graphicsdriver/metal/graphics_darwin.go | 20 ++++----- internal/graphicsdriver/opengl/graphics.go | 10 ++--- internal/mipmap/mipmap.go | 2 +- internal/ui/image.go | 6 +-- 16 files changed, 85 insertions(+), 85 deletions(-) diff --git a/image.go b/image.go index 374a63e6e..18544565c 100644 --- a/image.go +++ b/image.go @@ -262,7 +262,7 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) { }) } - i.image.DrawTriangles(srcs, vs, is, blend, i.adjustedBounds(), [graphics.ShaderImageCount]image.Rectangle{img.adjustedBounds()}, shader.shader, i.tmpUniforms, graphicsdriver.FillAll, canSkipMipmap(geoM, filter), false) + i.image.DrawTriangles(srcs, vs, is, blend, i.adjustedBounds(), [graphics.ShaderImageCount]image.Rectangle{img.adjustedBounds()}, shader.shader, i.tmpUniforms, graphicsdriver.FillRuleFillAll, canSkipMipmap(geoM, filter), false) } // Vertex represents a vertex passed to DrawTriangles. @@ -312,15 +312,15 @@ type FillRule int const ( // FillAll indicates all the triangles are rendered regardless of overlaps. - FillAll FillRule = FillRule(graphicsdriver.FillAll) + FillAll FillRule = FillRule(graphicsdriver.FillRuleFillAll) // NonZero means that triangles are rendered based on the non-zero rule. // If and only if the number of overlaps is not 0, the region is rendered. - NonZero FillRule = FillRule(graphicsdriver.NonZero) + NonZero FillRule = FillRule(graphicsdriver.FillRuleNonZero) // EvenOdd means that triangles are rendered based on the even-odd rule. // If and only if the number of overlaps is odd, the region is rendered. - EvenOdd FillRule = FillRule(graphicsdriver.EvenOdd) + EvenOdd FillRule = FillRule(graphicsdriver.FillRuleEvenOdd) ) // ColorScaleMode is the mode of color scales in vertices. @@ -816,7 +816,7 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR i.tmpUniforms = i.tmpUniforms[:0] i.tmpUniforms = shader.appendUniforms(i.tmpUniforms, options.Uniforms) - i.image.DrawTriangles(imgs, vs, is, blend, i.adjustedBounds(), srcRegions, shader.shader, i.tmpUniforms, graphicsdriver.FillAll, true, false) + i.image.DrawTriangles(imgs, vs, is, blend, i.adjustedBounds(), srcRegions, shader.shader, i.tmpUniforms, graphicsdriver.FillRuleFillAll, true, false) } // SubImage returns an image representing the portion of the image p visible through r. diff --git a/internal/atlas/image.go b/internal/atlas/image.go index fd68311b3..29854e495 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -151,7 +151,7 @@ func (b *backend) extendIfNeeded(width, height int) { vs := quadVertices(0, 0, float32(sw), float32(sh), 0, 0, float32(sw), float32(sh), 1, 1, 1, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, sw, sh) - newImg.DrawTriangles(srcs, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader.ensureShader(), nil, graphicsdriver.FillAll) + newImg.DrawTriangles(srcs, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader.ensureShader(), nil, graphicsdriver.FillRuleFillAll) b.image.Dispose() b.image = newImg @@ -175,7 +175,7 @@ func newClearedImage(width, height int, screen bool) *graphicscommand.Image { func clearImage(i *graphicscommand.Image, region image.Rectangle) { vs := quadVertices(float32(region.Min.X), float32(region.Min.Y), float32(region.Max.X), float32(region.Max.Y), 0, 0, 0, 0, 0, 0, 0, 0) is := graphics.QuadIndices() - i.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendClear, region, [graphics.ShaderImageCount]image.Rectangle{}, clearShader.ensureShader(), nil, graphicsdriver.FillAll) + i.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendClear, region, [graphics.ShaderImageCount]image.Rectangle{}, clearShader.ensureShader(), nil, graphicsdriver.FillRuleFillAll) } func (b *backend) clearPixels(region image.Rectangle) { @@ -348,7 +348,7 @@ func (i *Image) ensureIsolatedFromSource(backends []*backend) { is := graphics.QuadIndices() dr := image.Rect(0, 0, i.width, i.height) - newI.drawTriangles([graphics.ShaderImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillAll) + newI.drawTriangles([graphics.ShaderImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) newI.moveTo(i) } @@ -378,7 +378,7 @@ func (i *Image) putOnSourceBackend() { graphics.QuadVertices(vs, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, i.width, i.height) - newI.drawTriangles([graphics.ShaderImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillAll) + newI.drawTriangles([graphics.ShaderImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) newI.moveTo(i) i.usedAsSourceCount = 0 diff --git a/internal/atlas/image_test.go b/internal/atlas/image_test.go index 382ceaa23..d0ba251e8 100644 --- a/internal/atlas/image_test.go +++ b/internal/atlas/image_test.go @@ -105,7 +105,7 @@ func TestEnsureIsolatedFromSourceBackend(t *testing.T) { vs := quadVertices(size/2, size/2, size/4, size/4, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, size, size) - img4.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + img4.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := img4.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -113,7 +113,7 @@ func TestEnsureIsolatedFromSourceBackend(t *testing.T) { // img5 is not allocated now, but is allocated at DrawTriangles. vs = quadVertices(0, 0, size/2, size/2, 1) dr = image.Rect(0, 0, size/2, size/2) - img3.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img5}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + img3.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img5}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := img3.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -147,7 +147,7 @@ func TestEnsureIsolatedFromSourceBackend(t *testing.T) { // Check further drawing doesn't cause panic. // This bug was fixed by 03dcd948. vs = quadVertices(0, 0, size/2, size/2, 1) - img4.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + img4.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) } func TestReputOnSourceBackend(t *testing.T) { @@ -191,7 +191,7 @@ func TestReputOnSourceBackend(t *testing.T) { // Render onto img1. The count should not matter. for i := 0; i < 5; i++ { vs := quadVertices(size, size, 0, 0, 1) - img1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + img1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -203,7 +203,7 @@ func TestReputOnSourceBackend(t *testing.T) { for i := 0; i < atlas.BaseCountToPutOnSourceBackend*2; i++ { atlas.PutImagesOnSourceBackendForTesting() vs := quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -211,7 +211,7 @@ func TestReputOnSourceBackend(t *testing.T) { // Finally, img1 is on a source backend. atlas.PutImagesOnSourceBackendForTesting() vs := quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := img1.IsOnSourceBackendForTesting(), true; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -240,7 +240,7 @@ func TestReputOnSourceBackend(t *testing.T) { } vs = quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := img1.IsOnSourceBackendForTesting(), true; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -270,7 +270,7 @@ func TestReputOnSourceBackend(t *testing.T) { // Use img1 as a render target again. The count should not matter. for i := 0; i < 5; i++ { vs := quadVertices(size, size, 0, 0, 1) - img1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + img1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -282,7 +282,7 @@ func TestReputOnSourceBackend(t *testing.T) { atlas.PutImagesOnSourceBackendForTesting() img1.WritePixels(make([]byte, 4*size*size), image.Rect(0, 0, size, size)) vs := quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -291,7 +291,7 @@ func TestReputOnSourceBackend(t *testing.T) { // img1 is not on an atlas due to WritePixels. vs = quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := img1.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -300,7 +300,7 @@ func TestReputOnSourceBackend(t *testing.T) { for i := 0; i < atlas.BaseCountToPutOnSourceBackend*2; i++ { atlas.PutImagesOnSourceBackendForTesting() vs := quadVertices(size, size, 0, 0, 1) - img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + img0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := img3.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -403,7 +403,7 @@ func TestWritePixelsAfterDrawTriangles(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) dst.WritePixels(pix, image.Rect(0, 0, w, h)) pix = make([]byte, 4*w*h) @@ -450,7 +450,7 @@ func TestSmallImages(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) pix = make([]byte, 4*w*h) ok, err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, w, h)) @@ -497,7 +497,7 @@ func TestLongImages(t *testing.T) { vs := quadVertices(w, h, 0, 0, scale) is := graphics.QuadIndices() dr := image.Rect(0, 0, dstW, dstH) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) pix = make([]byte, 4*dstW*dstH) ok, err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, dstW, dstH)) @@ -613,7 +613,7 @@ func TestDeallocatedAndReputOnSourceBackend(t *testing.T) { vs := quadVertices(size, size, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, size, size) - src.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + src.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := src.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -622,7 +622,7 @@ func TestDeallocatedAndReputOnSourceBackend(t *testing.T) { for i := 0; i < atlas.BaseCountToPutOnSourceBackend/2; i++ { atlas.PutImagesOnSourceBackendForTesting() vs := quadVertices(size, size, 0, 0, 1) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := src.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -656,7 +656,7 @@ func TestImageIsNotReputOnSourceBackendWithoutUsingAsSource(t *testing.T) { // Call DrawTriangles multiple times. // The number of DrawTriangles doesn't matter as long as these are called in one frame. for i := 0; i < 2; i++ { - src2.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + src2.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) } if got, want := src2.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) @@ -675,7 +675,7 @@ func TestImageIsNotReputOnSourceBackendWithoutUsingAsSource(t *testing.T) { for i := 0; i < atlas.BaseCountToPutOnSourceBackend; i++ { atlas.PutImagesOnSourceBackendForTesting() vs := quadVertices(size, size, 0, 0, 1) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) if got, want := src2.IsOnSourceBackendForTesting(), false; got != want { t.Errorf("got: %v, want: %v", got, want) } @@ -801,14 +801,14 @@ func TestDestinationCountOverflow(t *testing.T) { // Use dst0 as a destination for a while. for i := 0; i < 31; i++ { - dst0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + dst0.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) atlas.PutImagesOnSourceBackendForTesting() } // Use dst0 as a source for a while. // As dst0 is used as a destination too many times (31 is a maximum), dst0's backend should never be a source backend. for i := 0; i < 100; i++ { - dst1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{dst0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + dst1.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{dst0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) atlas.PutImagesOnSourceBackendForTesting() if dst0.IsOnSourceBackendForTesting() { t.Errorf("dst0 cannot be on a source backend: %d", i) @@ -834,7 +834,7 @@ func TestIteratingImagesToPutOnSourceBackend(t *testing.T) { is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) for _, img := range srcs { - img.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + img.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) } atlas.PutImagesOnSourceBackendForTesting() @@ -842,7 +842,7 @@ func TestIteratingImagesToPutOnSourceBackend(t *testing.T) { // Check iterating the registered image works correctly. for i := 0; i < 100; i++ { for _, src := range srcs { - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) } atlas.PutImagesOnSourceBackendForTesting() } diff --git a/internal/atlas/shader_test.go b/internal/atlas/shader_test.go index b6bcb0bfc..f022f9b7f 100644 --- a/internal/atlas/shader_test.go +++ b/internal/atlas/shader_test.go @@ -37,12 +37,12 @@ func TestShaderFillTwice(t *testing.T) { dr := image.Rect(0, 0, w, h) g := ui.Get().GraphicsDriverForTesting() s0 := atlas.NewShader(etesting.ShaderProgramFill(0xff, 0xff, 0xff, 0xff)) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, s0, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, s0, nil, graphicsdriver.FillRuleFillAll) // Vertices must be recreated (#1755) vs = quadVertices(w, h, 0, 0, 1) s1 := atlas.NewShader(etesting.ShaderProgramFill(0x80, 0x80, 0x80, 0xff)) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, s1, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, s1, nil, graphicsdriver.FillRuleFillAll) pix := make([]byte, 4*w*h) ok, err := dst.ReadPixels(g, pix, image.Rect(0, 0, w, h)) @@ -69,11 +69,11 @@ func TestImageDrawTwice(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) // Vertices must be recreated (#1755) vs = quadVertices(w, h, 0, 0, 1) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{src1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) pix := make([]byte, 4*w*h) ok, err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, w, h)) @@ -97,7 +97,7 @@ func TestGCShader(t *testing.T) { vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*atlas.Image{}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll) // Ensure other objects are GCed, as GC appends deferred functions for collected objects. ensureGC() diff --git a/internal/buffered/image.go b/internal/buffered/image.go index 7bf2f0f96..de8eeb998 100644 --- a/internal/buffered/image.go +++ b/internal/buffered/image.go @@ -292,7 +292,7 @@ func (i *Image) syncPixelsIfNeeded() { srcs := [graphics.ShaderImageCount]*atlas.Image{whiteImage.img} dr := image.Rect(0, 0, i.width, i.height) blend := graphicsdriver.BlendCopy - i.img.DrawTriangles(srcs, vs, is, blend, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + i.img.DrawTriangles(srcs, vs, is, blend, dr, [graphics.ShaderImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) // TODO: Use clear if Go 1.21 is available. for pos := range i.dotsBuffer { diff --git a/internal/buffered/image_test.go b/internal/buffered/image_test.go index 87b0dcb07..4f36bb786 100644 --- a/internal/buffered/image_test.go +++ b/internal/buffered/image_test.go @@ -56,7 +56,7 @@ func TestUnsyncedPixels(t *testing.T) { is := graphics.QuadIndices() dr := image.Rect(0, 0, 16, 16) sr := [graphics.ShaderImageCount]image.Rectangle{image.Rect(0, 0, 16, 16)} - dst.DrawTriangles([graphics.ShaderImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, sr, atlas.NearestFilterShader, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, sr, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll) // Check the result is correct. var got [4]byte diff --git a/internal/graphicscommand/command.go b/internal/graphicscommand/command.go index dae55d6b4..07270fcf4 100644 --- a/internal/graphicscommand/command.go +++ b/internal/graphicscommand/command.go @@ -166,7 +166,7 @@ func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs if c.fillRule != fillRule { return false } - if c.fillRule != graphicsdriver.FillAll && mightOverlapDstRegions(c.vertices, vertices) { + if c.fillRule != graphicsdriver.FillRuleFillAll && mightOverlapDstRegions(c.vertices, vertices) { return false } return true diff --git a/internal/graphicscommand/image_test.go b/internal/graphicscommand/image_test.go index 5a00e0fed..17d416301 100644 --- a/internal/graphicscommand/image_test.go +++ b/internal/graphicscommand/image_test.go @@ -59,7 +59,7 @@ func TestClear(t *testing.T) { vs := quadVertices(w/2, h/2) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{src}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{src}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll) pix := make([]byte, 4*w*h) if err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), []graphicsdriver.PixelsArgs{ @@ -90,8 +90,8 @@ func TestWritePixelsPartAfterDrawTriangles(t *testing.T) { vs := quadVertices(w/2, h/2) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{clr}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillAll) - dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{clr}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll) bs := graphics.NewManagedBytes(4, func(bs []byte) { for i := range bs { bs[i] = 0 @@ -109,11 +109,11 @@ func TestShader(t *testing.T) { vs := quadVertices(w, h) is := graphics.QuadIndices() dr := image.Rect(0, 0, w, h) - dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{clr}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{clr}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll) g := ui.Get().GraphicsDriverForTesting() s := graphicscommand.NewShader(etesting.ShaderProgramFill(0xff, 0, 0, 0xff)) - dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillAll) + dst.DrawTriangles([graphics.ShaderImageCount]*graphicscommand.Image{}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll) pix := make([]byte, 4*w*h) if err := dst.ReadPixels(g, []graphicsdriver.PixelsArgs{ diff --git a/internal/graphicsdriver/directx/graphics11_windows.go b/internal/graphicsdriver/directx/graphics11_windows.go index e77b0cfe0..88ecd7776 100644 --- a/internal/graphicsdriver/directx/graphics11_windows.go +++ b/internal/graphicsdriver/directx/graphics11_windows.go @@ -542,7 +542,7 @@ func (g *graphics11) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphic }, }) - if err := dst.setAsRenderTarget(fillRule != graphicsdriver.FillAll); err != nil { + if err := dst.setAsRenderTarget(fillRule != graphicsdriver.FillRuleFillAll); err != nil { return err } @@ -552,7 +552,7 @@ func (g *graphics11) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphic return err } - if fillRule == graphicsdriver.FillAll { + if fillRule == graphicsdriver.FillRuleFillAll { bs, err := g.blendState(blend, noStencil) if err != nil { return err @@ -577,9 +577,9 @@ func (g *graphics11) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphic }) switch fillRule { - case graphicsdriver.FillAll: + case graphicsdriver.FillRuleFillAll: g.deviceContext.DrawIndexed(uint32(dstRegion.IndexCount), uint32(indexOffset), 0) - case graphicsdriver.NonZero: + case graphicsdriver.FillRuleNonZero: bs, err := g.blendState(blend, incrementStencil) if err != nil { return err @@ -591,7 +591,7 @@ func (g *graphics11) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphic } g.deviceContext.OMSetDepthStencilState(dss, 0) g.deviceContext.DrawIndexed(uint32(dstRegion.IndexCount), uint32(indexOffset), 0) - case graphicsdriver.EvenOdd: + case graphicsdriver.FillRuleEvenOdd: bs, err := g.blendState(blend, invertStencil) if err != nil { return err @@ -605,7 +605,7 @@ func (g *graphics11) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphic g.deviceContext.DrawIndexed(uint32(dstRegion.IndexCount), uint32(indexOffset), 0) } - if fillRule != graphicsdriver.FillAll { + if fillRule != graphicsdriver.FillRuleFillAll { bs, err := g.blendState(blend, drawWithStencil) if err != nil { return err diff --git a/internal/graphicsdriver/directx/graphics12_windows.go b/internal/graphicsdriver/directx/graphics12_windows.go index 634b5c06d..2877046e5 100644 --- a/internal/graphicsdriver/directx/graphics12_windows.go +++ b/internal/graphicsdriver/directx/graphics12_windows.go @@ -1092,7 +1092,7 @@ func (g *graphics12) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics. // Release constant buffers when too many ones will be created. numPipelines := 1 - if fillRule != graphicsdriver.FillAll { + if fillRule != graphicsdriver.FillRuleFillAll { numPipelines = 2 } if len(g.pipelineStates.constantBuffers[g.frameIndex])+numPipelines > numDescriptorsPerFrame { @@ -1124,7 +1124,7 @@ func (g *graphics12) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics. g.drawCommandList.ResourceBarrier(resourceBarriers) } - if err := dst.setAsRenderTarget(g.drawCommandList, g.device, fillRule != graphicsdriver.FillAll); err != nil { + if err := dst.setAsRenderTarget(g.drawCommandList, g.device, fillRule != graphicsdriver.FillRuleFillAll); err != nil { return err } diff --git a/internal/graphicsdriver/directx/pipeline12_windows.go b/internal/graphicsdriver/directx/pipeline12_windows.go index c06b4d9d4..41e019fe5 100644 --- a/internal/graphicsdriver/directx/pipeline12_windows.go +++ b/internal/graphicsdriver/directx/pipeline12_windows.go @@ -289,7 +289,7 @@ func (p *pipelineStates) drawTriangles(device *_ID3D12Device, commandList *_ID3D } commandList.SetGraphicsRootDescriptorTable(2, sh) - if fillRule == graphicsdriver.FillAll { + if fillRule == graphicsdriver.FillRuleFillAll { s, err := shader.pipelineState(blend, noStencil, screen) if err != nil { return err @@ -307,16 +307,16 @@ func (p *pipelineStates) drawTriangles(device *_ID3D12Device, commandList *_ID3D }, }) switch fillRule { - case graphicsdriver.FillAll: + case graphicsdriver.FillRuleFillAll: commandList.DrawIndexedInstanced(uint32(dstRegion.IndexCount), 1, uint32(indexOffset), 0, 0) - case graphicsdriver.NonZero: + case graphicsdriver.FillRuleNonZero: s, err := shader.pipelineState(blend, incrementStencil, screen) if err != nil { return err } commandList.SetPipelineState(s) commandList.DrawIndexedInstanced(uint32(dstRegion.IndexCount), 1, uint32(indexOffset), 0, 0) - case graphicsdriver.EvenOdd: + case graphicsdriver.FillRuleEvenOdd: s, err := shader.pipelineState(blend, invertStencil, screen) if err != nil { return err @@ -325,7 +325,7 @@ func (p *pipelineStates) drawTriangles(device *_ID3D12Device, commandList *_ID3D commandList.DrawIndexedInstanced(uint32(dstRegion.IndexCount), 1, uint32(indexOffset), 0, 0) } - if fillRule != graphicsdriver.FillAll { + if fillRule != graphicsdriver.FillRuleFillAll { s, err := shader.pipelineState(blend, drawWithStencil, screen) if err != nil { return err diff --git a/internal/graphicsdriver/graphics.go b/internal/graphicsdriver/graphics.go index d15d5defb..67b55d46f 100644 --- a/internal/graphicsdriver/graphics.go +++ b/internal/graphicsdriver/graphics.go @@ -30,19 +30,19 @@ type DstRegion struct { type FillRule int const ( - FillAll FillRule = iota - NonZero - EvenOdd + FillRuleFillAll FillRule = iota + FillRuleNonZero + FillRuleEvenOdd ) func (f FillRule) String() string { switch f { - case FillAll: - return "FillAll" - case NonZero: - return "NonZero" - case EvenOdd: - return "EvenOdd" + case FillRuleFillAll: + return "FillRuleFillAll" + case FillRuleNonZero: + return "FillRuleNonZero" + case FillRuleEvenOdd: + return "FillRuleEvenOdd" default: return fmt.Sprintf("FillRule(%d)", f) } diff --git a/internal/graphicsdriver/metal/graphics_darwin.go b/internal/graphicsdriver/metal/graphics_darwin.go index 1ea2bd50b..3c7659226 100644 --- a/internal/graphicsdriver/metal/graphics_darwin.go +++ b/internal/graphicsdriver/metal/graphics_darwin.go @@ -471,7 +471,7 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs // When preparing a stencil buffer, flush the current render command encoder // to make sure the stencil buffer is cleared when loading. // TODO: What about clearing the stencil buffer by vertices? - if g.lastDst != dst || g.lastFillRule != fillRule || fillRule != graphicsdriver.FillAll { + if g.lastDst != dst || g.lastFillRule != fillRule || fillRule != graphicsdriver.FillRuleFillAll { g.flushRenderCommandEncoderIfNeeded() } g.lastDst = dst @@ -497,7 +497,7 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs rpd.ColorAttachments[0].Texture = t rpd.ColorAttachments[0].ClearColor = mtl.ClearColor{} - if fillRule != graphicsdriver.FillAll { + if fillRule != graphicsdriver.FillRuleFillAll { dst.ensureStencil() rpd.StencilAttachment.LoadAction = mtl.LoadActionClear rpd.StencilAttachment.StoreAction = mtl.StoreActionDontCare @@ -544,26 +544,26 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs drawWithStencilRpss mtl.RenderPipelineState ) switch fillRule { - case graphicsdriver.FillAll: + case graphicsdriver.FillRuleFillAll: s, err := shader.RenderPipelineState(&g.view, blend, noStencil, dst.screen) if err != nil { return err } noStencilRpss = s - case graphicsdriver.NonZero: + case graphicsdriver.FillRuleNonZero: s, err := shader.RenderPipelineState(&g.view, blend, incrementStencil, dst.screen) if err != nil { return err } incrementStencilRpss = s - case graphicsdriver.EvenOdd: + case graphicsdriver.FillRuleEvenOdd: s, err := shader.RenderPipelineState(&g.view, blend, invertStencil, dst.screen) if err != nil { return err } invertStencilRpss = s } - if fillRule != graphicsdriver.FillAll { + if fillRule != graphicsdriver.FillRuleFillAll { s, err := shader.RenderPipelineState(&g.view, blend, drawWithStencil, dst.screen) if err != nil { return err @@ -580,20 +580,20 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs }) switch fillRule { - case graphicsdriver.FillAll: + case graphicsdriver.FillRuleFillAll: g.rce.SetDepthStencilState(g.dsss[noStencil]) g.rce.SetRenderPipelineState(noStencilRpss) g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0)))) - case graphicsdriver.NonZero: + case graphicsdriver.FillRuleNonZero: g.rce.SetDepthStencilState(g.dsss[incrementStencil]) g.rce.SetRenderPipelineState(incrementStencilRpss) g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0)))) - case graphicsdriver.EvenOdd: + case graphicsdriver.FillRuleEvenOdd: g.rce.SetDepthStencilState(g.dsss[invertStencil]) g.rce.SetRenderPipelineState(invertStencilRpss) g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0)))) } - if fillRule != graphicsdriver.FillAll { + if fillRule != graphicsdriver.FillRuleFillAll { g.rce.SetDepthStencilState(g.dsss[drawWithStencil]) g.rce.SetRenderPipelineState(drawWithStencilRpss) g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0)))) diff --git a/internal/graphicsdriver/opengl/graphics.go b/internal/graphicsdriver/opengl/graphics.go index 44b20d620..6f1cd44de 100644 --- a/internal/graphicsdriver/opengl/graphics.go +++ b/internal/graphicsdriver/opengl/graphics.go @@ -259,7 +259,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics. } g.uniformVars = g.uniformVars[:0] - if fillRule != graphicsdriver.FillAll { + if fillRule != graphicsdriver.FillRuleFillAll { if err := destination.ensureStencilBuffer(); err != nil { return err } @@ -274,14 +274,14 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics. int32(dstRegion.Region.Dy()), ) switch fillRule { - case graphicsdriver.NonZero: + case graphicsdriver.FillRuleNonZero: g.context.ctx.Clear(gl.STENCIL_BUFFER_BIT) g.context.ctx.StencilFunc(gl.ALWAYS, 0x00, 0xff) g.context.ctx.StencilOpSeparate(gl.FRONT, gl.KEEP, gl.KEEP, gl.INCR_WRAP) g.context.ctx.StencilOpSeparate(gl.BACK, gl.KEEP, gl.KEEP, gl.DECR_WRAP) g.context.ctx.ColorMask(false, false, false, false) g.context.ctx.DrawElements(gl.TRIANGLES, int32(dstRegion.IndexCount), gl.UNSIGNED_INT, indexOffset*int(unsafe.Sizeof(uint32(0)))) - case graphicsdriver.EvenOdd: + case graphicsdriver.FillRuleEvenOdd: g.context.ctx.Clear(gl.STENCIL_BUFFER_BIT) g.context.ctx.StencilFunc(gl.ALWAYS, 0x00, 0xff) g.context.ctx.StencilOpSeparate(gl.FRONT_AND_BACK, gl.KEEP, gl.KEEP, gl.INVERT) @@ -289,7 +289,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics. g.context.ctx.DrawElements(gl.TRIANGLES, int32(dstRegion.IndexCount), gl.UNSIGNED_INT, indexOffset*int(unsafe.Sizeof(uint32(0)))) } - if fillRule != graphicsdriver.FillAll { + if fillRule != graphicsdriver.FillRuleFillAll { g.context.ctx.StencilFunc(gl.NOTEQUAL, 0x00, 0xff) g.context.ctx.StencilOpSeparate(gl.FRONT_AND_BACK, gl.KEEP, gl.KEEP, gl.KEEP) g.context.ctx.ColorMask(true, true, true, true) @@ -298,7 +298,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics. indexOffset += dstRegion.IndexCount } - if fillRule != graphicsdriver.FillAll { + if fillRule != graphicsdriver.FillRuleFillAll { g.context.ctx.Disable(gl.STENCIL_TEST) } diff --git a/internal/mipmap/mipmap.go b/internal/mipmap/mipmap.go index 47ca0cad7..6476ab2a0 100644 --- a/internal/mipmap/mipmap.go +++ b/internal/mipmap/mipmap.go @@ -187,7 +187,7 @@ func (m *Mipmap) level(level int) *buffered.Image { s := buffered.NewImage(w2, h2, m.imageType) dstRegion := image.Rect(0, 0, w2, h2) - s.DrawTriangles([graphics.ShaderImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderImageCount]image.Rectangle{}, shader, nil, graphicsdriver.FillAll) + s.DrawTriangles([graphics.ShaderImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderImageCount]image.Rectangle{}, shader, nil, graphicsdriver.FillRuleFillAll) m.setImg(level, s) return m.imgs[level] diff --git a/internal/ui/image.go b/internal/ui/image.go index 093bfb654..31db743ab 100644 --- a/internal/ui/image.go +++ b/internal/ui/image.go @@ -183,7 +183,7 @@ func (i *Image) Fill(r, g, b, a float32, region image.Rectangle) { blend = graphicsdriver.BlendSourceOver } // i.lastBlend is updated in DrawTriangles. - i.DrawTriangles(srcs, i.tmpVerticesForFill, is, blend, region, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillAll, true, false) + i.DrawTriangles(srcs, i.tmpVerticesForFill, is, blend, region, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false) } type bigOffscreenImage struct { @@ -252,7 +252,7 @@ func (i *bigOffscreenImage) drawTriangles(srcs [graphics.ShaderImageCount]*Image 1, 1, 1, 1) is := graphics.QuadIndices() dstRegion := image.Rect(0, 0, i.region.Dx()*bigOffscreenScale, i.region.Dy()*bigOffscreenScale) - i.image.DrawTriangles(srcs, i.tmpVerticesForCopying, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillAll, true, false) + i.image.DrawTriangles(srcs, i.tmpVerticesForCopying, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false) } for idx := 0; idx < len(vertices); idx += graphics.VertexFloatCount { @@ -300,7 +300,7 @@ func (i *bigOffscreenImage) flush() { if i.blend != graphicsdriver.BlendSourceOver { blend = graphicsdriver.BlendCopy } - i.orig.DrawTriangles(srcs, i.tmpVerticesForFlushing, is, blend, dstRegion, [graphics.ShaderImageCount]image.Rectangle{}, LinearFilterShader, nil, graphicsdriver.FillAll, true, false) + i.orig.DrawTriangles(srcs, i.tmpVerticesForFlushing, is, blend, dstRegion, [graphics.ShaderImageCount]image.Rectangle{}, LinearFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false) i.image.clear() i.dirty = false From d37301eeeb169752cf496215747cad85df3bb5af Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 8 Jun 2024 14:34:01 +0900 Subject: [PATCH 79/81] .github/workflow: disable wasm tests on Windows temporarily Updates #2982 --- .github/workflows/test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d54493e66..7bf5e1851 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -165,9 +165,10 @@ jobs: env GOARCH=386 EBITENGINE_DIRECTX=version=12 go test -shuffle=on -v ./... - name: go test (Wasm) - if: runner.os != 'macOS' + if: ${{ runner.os != 'macOS' && runner.os != 'Windows' }} run: | # Wasm tests don't work on macOS with the headless mode, and the headless mode doesn't work in GitHub Actions (#2972). + # Wasm tests don't work on Windows well due to mysterious timeouts (#2982). env GOOS=js GOARCH=wasm cleanenv -remove-prefix GITHUB_ -remove-prefix JAVA_ -remove-prefix PSModulePath -remove-prefix STATS_ -remove-prefix RUNNER_ -- go test -shuffle=on -v ./... - name: Install ebitenmobile From b121468991643ec929db3cdc5efcb4176dea4fda Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sat, 8 Jun 2024 12:06:00 +0900 Subject: [PATCH 80/81] ebiten: add `FillRuleFillAll`, `FillRuleEvenOdd`, and `FillRuleNonZero` This change also deprecates the existing constants. Closes #3006 --- colorm/draw.go | 4 +-- examples/vector/main.go | 16 +++++------ image.go | 33 +++++++++++++++++----- image_test.go | 20 ++++++------- internal/processtest/testdata/issue2138.go | 4 +-- vector/path.go | 6 ++-- 6 files changed, 51 insertions(+), 32 deletions(-) diff --git a/colorm/draw.go b/colorm/draw.go index 56408fbb6..3992f527d 100644 --- a/colorm/draw.go +++ b/colorm/draw.go @@ -72,11 +72,11 @@ type DrawTrianglesOptions struct { // FillRule indicates the rule how an overlapped region is rendered. // - // The rules NonZero and EvenOdd are useful when you want to render a complex polygon. + // The rules FileRuleNonZero and FillRuleEvenOdd are useful when you want to render a complex polygon. // A complex polygon is a non-convex polygon like a concave polygon, a polygon with holes, or a self-intersecting polygon. // See examples/vector for actual usages. // - // The default (zero) value is ebiten.FillAll. + // The default (zero) value is ebiten.FillRuleFillAll. FillRule ebiten.FillRule // AntiAlias indicates whether the rendering uses anti-alias or not. diff --git a/examples/vector/main.go b/examples/vector/main.go index 674388d62..02ac37350 100644 --- a/examples/vector/main.go +++ b/examples/vector/main.go @@ -141,14 +141,14 @@ func drawEbitenText(screen *ebiten.Image, x, y int, aa bool, line bool) { op := &ebiten.DrawTrianglesOptions{} op.AntiAlias = aa - // For strokes (AppendVerticesAndIndicesForStroke), FillAll and NonZero work. + // For strokes (AppendVerticesAndIndicesForStroke), FillRuleFillAll and FillRuleNonZero work. // - // For filling (AppendVerticesAndIndicesForFilling), NonZero and EvenOdd work. - // NonZero and EvenOdd differ when rendering a complex polygons with self-intersections and/or holes. + // For filling (AppendVerticesAndIndicesForFilling), FillRuleNonZero and FillRuleEvenOdd work. + // FillRuleNonZero and FillRuleEvenOdd differ when rendering a complex polygons with self-intersections and/or holes. // See https://en.wikipedia.org/wiki/Nonzero-rule and https://en.wikipedia.org/wiki/Even%E2%80%93odd_rule . // - // For simplicity, this example always uses NonZero, whichever strokes or filling is done. - op.FillRule = ebiten.NonZero + // For simplicity, this example always uses FillRuleNonZero, whichever strokes or filling is done. + op.FillRule = ebiten.FillRuleNonZero screen.DrawTriangles(vs, is, whiteSubImage, op) } @@ -203,7 +203,7 @@ func drawEbitenLogo(screen *ebiten.Image, x, y int, aa bool, line bool) { op := &ebiten.DrawTrianglesOptions{} op.AntiAlias = aa - op.FillRule = ebiten.NonZero + op.FillRule = ebiten.FillRuleNonZero screen.DrawTriangles(vs, is, whiteSubImage, op) } @@ -245,7 +245,7 @@ func drawArc(screen *ebiten.Image, count int, aa bool, line bool) { op := &ebiten.DrawTrianglesOptions{} op.AntiAlias = aa - op.FillRule = ebiten.NonZero + op.FillRule = ebiten.FillRuleNonZero screen.DrawTriangles(vs, is, whiteSubImage, op) } @@ -300,7 +300,7 @@ func drawWave(screen *ebiten.Image, counter int, aa bool, line bool) { op := &ebiten.DrawTrianglesOptions{} op.AntiAlias = aa - op.FillRule = ebiten.NonZero + op.FillRule = ebiten.FillRuleNonZero screen.DrawTriangles(vs, is, whiteSubImage, op) } diff --git a/image.go b/image.go index 18544565c..e60e40e81 100644 --- a/image.go +++ b/image.go @@ -310,17 +310,36 @@ const ( // FillRule is the rule whether an overlapped region is rendered with DrawTriangles(Shader). type FillRule int +const ( + // FillRuleFillAll indicates all the triangles are rendered regardless of overlaps. + FillRuleFillAll FillRule = FillRule(graphicsdriver.FillRuleFillAll) + + // FillRuleNonZero means that triangles are rendered based on the non-zero rule. + // If and only if the number of overlaps is not 0, the region is rendered. + FillRuleNonZero FillRule = FillRule(graphicsdriver.FillRuleNonZero) + + // FillRuleEvenOdd means that triangles are rendered based on the even-odd rule. + // If and only if the number of overlaps is odd, the region is rendered. + FillRuleEvenOdd FillRule = FillRule(graphicsdriver.FillRuleEvenOdd) +) + const ( // FillAll indicates all the triangles are rendered regardless of overlaps. - FillAll FillRule = FillRule(graphicsdriver.FillRuleFillAll) + // + // Deprecated: as of v2.8. Use FillRuleFillAll instead. + FillAll = FillRuleFillAll // NonZero means that triangles are rendered based on the non-zero rule. // If and only if the number of overlaps is not 0, the region is rendered. - NonZero FillRule = FillRule(graphicsdriver.FillRuleNonZero) + // + // Deprecated: as of v2.8. Use FillRuleNonZero instead. + NonZero = FillRuleNonZero // EvenOdd means that triangles are rendered based on the even-odd rule. // If and only if the number of overlaps is odd, the region is rendered. - EvenOdd FillRule = FillRule(graphicsdriver.FillRuleEvenOdd) + // + // Deprecated: as of v2.8. Use FillRuleEvenOdd instead. + EvenOdd = FillRuleEvenOdd ) // ColorScaleMode is the mode of color scales in vertices. @@ -371,11 +390,11 @@ type DrawTrianglesOptions struct { // FillRule indicates the rule how an overlapped region is rendered. // - // The rules NonZero and EvenOdd are useful when you want to render a complex polygon. + // The rules FillRuleNonZero and FillRuleEvenOdd are useful when you want to render a complex polygon. // A complex polygon is a non-convex polygon like a concave polygon, a polygon with holes, or a self-intersecting polygon. // See examples/vector for actual usages. // - // The default (zero) value is FillAll. + // The default (zero) value is FillRuleFillAll. FillRule FillRule // AntiAlias indicates whether the rendering uses anti-alias or not. @@ -547,11 +566,11 @@ type DrawTrianglesShaderOptions struct { // FillRule indicates the rule how an overlapped region is rendered. // - // The rules NonZero and EvenOdd are useful when you want to render a complex polygon. + // The rules FillRuleNonZero and FillRuleEvenOdd are useful when you want to render a complex polygon. // A complex polygon is a non-convex polygon like a concave polygon, a polygon with holes, or a self-intersecting polygon. // See examples/vector for actual usages. // - // The default (zero) value is FillAll. + // The default (zero) value is FillRuleFillAll. FillRule FillRule // AntiAlias indicates whether the rendering uses anti-alias or not. diff --git a/image_test.go b/image_test.go index 660418a48..2f58e7f36 100644 --- a/image_test.go +++ b/image_test.go @@ -2696,7 +2696,7 @@ func TestImageEvenOdd(t *testing.T) { // Draw all the vertices once. The even-odd rule is applied for all the vertices once. dst := ebiten.NewImage(16, 16) op := &ebiten.DrawTrianglesOptions{ - FillRule: ebiten.EvenOdd, + FillRule: ebiten.FillRuleEvenOdd, } dst.DrawTriangles(append(append(vs0, vs1...), vs2...), append(append(is0, is1...), is2...), emptySubImage, op) for j := 0; j < 16; j++ { @@ -2794,15 +2794,15 @@ func TestImageEvenOdd(t *testing.T) { } func TestImageFillRule(t *testing.T) { - for _, fillRule := range []ebiten.FillRule{ebiten.FillAll, ebiten.NonZero, ebiten.EvenOdd} { + for _, fillRule := range []ebiten.FillRule{ebiten.FillRuleFillAll, ebiten.FillRuleNonZero, ebiten.FillRuleEvenOdd} { fillRule := fillRule var name string switch fillRule { - case ebiten.FillAll: + case ebiten.FillRuleFillAll: name = "FillAll" - case ebiten.NonZero: + case ebiten.FillRuleNonZero: name = "NonZero" - case ebiten.EvenOdd: + case ebiten.FillRuleEvenOdd: name = "EvenOdd" } t.Run(name, func(t *testing.T) { @@ -2885,11 +2885,11 @@ func TestImageFillRule(t *testing.T) { var want color.RGBA switch { case 2 <= i && i < 7 && 2 <= j && j < 7: - if fillRule != ebiten.EvenOdd { + if fillRule != ebiten.FillRuleEvenOdd { want = color.RGBA{G: 0xff, A: 0xff} } case 9 <= i && i < 14 && 9 <= j && j < 14: - if fillRule == ebiten.FillAll { + if fillRule == ebiten.FillRuleFillAll { want = color.RGBA{B: 0xff, A: 0xff} } case 1 <= i && i < 15 && 1 <= j && j < 15: @@ -2922,11 +2922,11 @@ func TestImageFillRule(t *testing.T) { var want color.RGBA switch { case 3 <= i && i < 8 && 3 <= j && j < 8: - if fillRule != ebiten.EvenOdd { + if fillRule != ebiten.FillRuleEvenOdd { want = color.RGBA{G: 0xff, A: 0xff} } case 10 <= i && i < 15 && 10 <= j && j < 15: - if fillRule == ebiten.FillAll { + if fillRule == ebiten.FillRuleFillAll { want = color.RGBA{B: 0xff, A: 0xff} } case 2 <= i && i < 16 && 2 <= j && j < 16: @@ -3726,7 +3726,7 @@ func TestImageTooManyConstantBuffersInDirectX(t *testing.T) { dst0 := ebiten.NewImage(16, 16) dst1 := ebiten.NewImage(16, 16) op := &ebiten.DrawTrianglesOptions{ - FillRule: ebiten.EvenOdd, + FillRule: ebiten.FillRuleEvenOdd, } for i := 0; i < 100; i++ { dst0.DrawTriangles(vs, is, src, op) diff --git a/internal/processtest/testdata/issue2138.go b/internal/processtest/testdata/issue2138.go index 34e8dcb98..18f3ab214 100644 --- a/internal/processtest/testdata/issue2138.go +++ b/internal/processtest/testdata/issue2138.go @@ -59,7 +59,7 @@ func (g *Game) Update() error { } func (g *Game) Draw(screen *ebiten.Image) { - // Before the fix, some complex renderings with EvenOdd might cause a DirectX error like this (#2138): + // Before the fix, some complex renderings with FillRuleEvenOdd might cause a DirectX error like this (#2138): // panic: directx: IDXGISwapChain4::Present failed: HRESULT(2289696773) screen.DrawImage(debugCircleImage, nil) @@ -74,7 +74,7 @@ func (g *Game) Draw(screen *ebiten.Image) { p.Arc(100, 100, 6, 0, 2*math.Pi, vector.Clockwise) filling, indicies := p.AppendVerticesAndIndicesForFilling(nil, nil) screen.DrawTriangles(filling, indicies, whiteTextureImage, &ebiten.DrawTrianglesOptions{ - FillRule: ebiten.EvenOdd, + FillRule: ebiten.FillRuleEvenOdd, }) } diff --git a/vector/path.go b/vector/path.go index 0474d75de..b381b15ad 100644 --- a/vector/path.go +++ b/vector/path.go @@ -396,7 +396,7 @@ func (p *Path) Close() { // // The returned vertice's SrcX and SrcY are 0, and ColorR, ColorG, ColorB, and ColorA are 1. // -// The returned values are intended to be passed to DrawTriangles or DrawTrianglesShader with the fill rule NonZero or EvenOdd +// The returned values are intended to be passed to DrawTriangles or DrawTrianglesShader with FileRuleNonZero or FillRuleEvenOdd // in order to render a complex polygon like a concave polygon, a polygon with holes, or a self-intersecting polygon. // // The returned vertices and indices should be rendered with a solid (non-transparent) color with the default Blend (source-over). @@ -480,7 +480,7 @@ type StrokeOptions struct { // The returned vertice's SrcX and SrcY are 0, and ColorR, ColorG, ColorB, and ColorA are 1. // // The returned values are intended to be passed to DrawTriangles or DrawTrianglesShader with a solid (non-transparent) color -// with FillAll or NonZero fill rule, not EvenOdd fill rule. +// with FillRuleFillAll or FillRuleNonZero, not FileRuleEvenOdd. func (p *Path) AppendVerticesAndIndicesForStroke(vertices []ebiten.Vertex, indices []uint16, op *StrokeOptions) ([]ebiten.Vertex, []uint16) { if op == nil { return vertices, indices @@ -536,7 +536,7 @@ func (p *Path) AppendVerticesAndIndicesForStroke(vertices []ebiten.Vertex, indic ColorA: 1, }) } - // All the triangles are rendered in clockwise order to enable NonZero filling rule (#2833). + // All the triangles are rendered in clockwise order to enable FillRuleNonZero (#2833). indices = append(indices, idx, idx+1, idx+2, idx+1, idx+3, idx+2) // Add line joints. From aa6bc1690e19453a6c10394071ca6374d95c48d2 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Sun, 9 Jun 2024 11:54:49 +0900 Subject: [PATCH 81/81] .github/workflows: typo Updates #2972 --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7bf5e1851..cdd2c5351 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -167,7 +167,7 @@ jobs: - name: go test (Wasm) if: ${{ runner.os != 'macOS' && runner.os != 'Windows' }} run: | - # Wasm tests don't work on macOS with the headless mode, and the headless mode doesn't work in GitHub Actions (#2972). + # Wasm tests don't work on macOS with the headless mode enabled, but the headless mode cannot be disabled in GitHub Actions (#2972). # Wasm tests don't work on Windows well due to mysterious timeouts (#2982). env GOOS=js GOARCH=wasm cleanenv -remove-prefix GITHUB_ -remove-prefix JAVA_ -remove-prefix PSModulePath -remove-prefix STATS_ -remove-prefix RUNNER_ -- go test -shuffle=on -v ./...