win32.go (8942B)
1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build windows 6 7 package gldriver 8 9 import ( 10 "errors" 11 "fmt" 12 "runtime" 13 "syscall" 14 "unsafe" 15 16 "golang.org/x/exp/shiny/driver/internal/win32" 17 "golang.org/x/exp/shiny/screen" 18 "golang.org/x/mobile/event/key" 19 "golang.org/x/mobile/event/lifecycle" 20 "golang.org/x/mobile/event/mouse" 21 "golang.org/x/mobile/event/paint" 22 "golang.org/x/mobile/event/size" 23 "golang.org/x/mobile/gl" 24 ) 25 26 // TODO: change this to true, after manual testing on Win32. 27 const useLifecycler = false 28 29 // TODO: change this to true, after manual testing on Win32. 30 const handleSizeEventsAtChannelReceive = false 31 32 func main(f func(screen.Screen)) error { 33 return win32.Main(func() { f(theScreen) }) 34 } 35 36 var ( 37 eglGetPlatformDisplayEXT = gl.LibEGL.NewProc("eglGetPlatformDisplayEXT") 38 eglInitialize = gl.LibEGL.NewProc("eglInitialize") 39 eglChooseConfig = gl.LibEGL.NewProc("eglChooseConfig") 40 eglGetError = gl.LibEGL.NewProc("eglGetError") 41 eglBindAPI = gl.LibEGL.NewProc("eglBindAPI") 42 eglCreateWindowSurface = gl.LibEGL.NewProc("eglCreateWindowSurface") 43 eglCreateContext = gl.LibEGL.NewProc("eglCreateContext") 44 eglMakeCurrent = gl.LibEGL.NewProc("eglMakeCurrent") 45 eglSwapInterval = gl.LibEGL.NewProc("eglSwapInterval") 46 eglDestroySurface = gl.LibEGL.NewProc("eglDestroySurface") 47 eglSwapBuffers = gl.LibEGL.NewProc("eglSwapBuffers") 48 ) 49 50 type eglConfig uintptr // void* 51 52 type eglInt int32 53 54 var rgb888 = [...]eglInt{ 55 _EGL_RENDERABLE_TYPE, _EGL_OPENGL_ES2_BIT, 56 _EGL_SURFACE_TYPE, _EGL_WINDOW_BIT, 57 _EGL_BLUE_SIZE, 8, 58 _EGL_GREEN_SIZE, 8, 59 _EGL_RED_SIZE, 8, 60 _EGL_DEPTH_SIZE, 16, 61 _EGL_STENCIL_SIZE, 8, 62 _EGL_NONE, 63 } 64 65 type ctxWin32 struct { 66 ctx uintptr 67 display uintptr // EGLDisplay 68 surface uintptr // EGLSurface 69 } 70 71 func newWindow(opts *screen.NewWindowOptions) (uintptr, error) { 72 w, err := win32.NewWindow(opts) 73 if err != nil { 74 return 0, err 75 } 76 return uintptr(w), nil 77 } 78 79 func initWindow(w *windowImpl) { 80 w.glctx, w.worker = gl.NewContext() 81 } 82 83 func showWindow(w *windowImpl) { 84 // Show makes an initial call to sizeEvent (via win32.SizeEvent), where 85 // we setup the EGL surface and GL context. 86 win32.Show(syscall.Handle(w.id)) 87 } 88 89 func closeWindow(id uintptr) {} // TODO 90 91 func drawLoop(w *windowImpl) { 92 runtime.LockOSThread() 93 94 display := w.ctx.(ctxWin32).display 95 surface := w.ctx.(ctxWin32).surface 96 ctx := w.ctx.(ctxWin32).ctx 97 98 if ret, _, _ := eglMakeCurrent.Call(display, surface, surface, ctx); ret == 0 { 99 panic(fmt.Sprintf("eglMakeCurrent failed: %v", eglErr())) 100 } 101 102 // TODO(crawshaw): exit this goroutine on Release. 103 workAvailable := w.worker.WorkAvailable() 104 for { 105 select { 106 case <-workAvailable: 107 w.worker.DoWork() 108 case <-w.publish: 109 loop: 110 for { 111 select { 112 case <-workAvailable: 113 w.worker.DoWork() 114 default: 115 break loop 116 } 117 } 118 if ret, _, _ := eglSwapBuffers.Call(display, surface); ret == 0 { 119 panic(fmt.Sprintf("eglSwapBuffers failed: %v", eglErr())) 120 } 121 w.publishDone <- screen.PublishResult{} 122 } 123 } 124 } 125 126 func init() { 127 win32.SizeEvent = sizeEvent 128 win32.PaintEvent = paintEvent 129 win32.MouseEvent = mouseEvent 130 win32.KeyEvent = keyEvent 131 win32.LifecycleEvent = lifecycleEvent 132 } 133 134 func lifecycleEvent(hwnd syscall.Handle, to lifecycle.Stage) { 135 theScreen.mu.Lock() 136 w := theScreen.windows[uintptr(hwnd)] 137 theScreen.mu.Unlock() 138 139 if w.lifecycleStage == to { 140 return 141 } 142 w.Send(lifecycle.Event{ 143 From: w.lifecycleStage, 144 To: to, 145 DrawContext: w.glctx, 146 }) 147 w.lifecycleStage = to 148 } 149 150 func mouseEvent(hwnd syscall.Handle, e mouse.Event) { 151 theScreen.mu.Lock() 152 w := theScreen.windows[uintptr(hwnd)] 153 theScreen.mu.Unlock() 154 155 w.Send(e) 156 } 157 158 func keyEvent(hwnd syscall.Handle, e key.Event) { 159 theScreen.mu.Lock() 160 w := theScreen.windows[uintptr(hwnd)] 161 theScreen.mu.Unlock() 162 163 w.Send(e) 164 } 165 166 func paintEvent(hwnd syscall.Handle, e paint.Event) { 167 theScreen.mu.Lock() 168 w := theScreen.windows[uintptr(hwnd)] 169 theScreen.mu.Unlock() 170 171 if w.ctx == nil { 172 // Sometimes a paint event comes in before initial 173 // window size is set. Ignore it. 174 return 175 } 176 177 // TODO: the paint.Event should have External: true. 178 w.Send(paint.Event{}) 179 } 180 181 func sizeEvent(hwnd syscall.Handle, e size.Event) { 182 theScreen.mu.Lock() 183 w := theScreen.windows[uintptr(hwnd)] 184 theScreen.mu.Unlock() 185 186 if w.ctx == nil { 187 // This is the initial size event on window creation. 188 // Create an EGL surface and spin up a GL context. 189 if err := createEGLSurface(hwnd, w); err != nil { 190 panic(err) 191 } 192 go drawLoop(w) 193 } 194 195 if !handleSizeEventsAtChannelReceive { 196 w.szMu.Lock() 197 w.sz = e 198 w.szMu.Unlock() 199 } 200 201 w.Send(e) 202 203 if handleSizeEventsAtChannelReceive { 204 return 205 } 206 207 // Screen is dirty, generate a paint event. 208 // 209 // The sizeEvent function is called on the goroutine responsible for 210 // calling the GL worker.DoWork. When compiling with -tags gldebug, 211 // these GL calls are blocking (so we can read the error message), so 212 // to make progress they need to happen on another goroutine. 213 go func() { 214 // TODO: this call to Viewport is not right, but is very hard to 215 // do correctly with our async events channel model. We want 216 // the call to Viewport to be made the instant before the 217 // paint.Event is received. 218 w.glctxMu.Lock() 219 w.glctx.Viewport(0, 0, e.WidthPx, e.HeightPx) 220 w.glctx.ClearColor(0, 0, 0, 1) 221 w.glctx.Clear(gl.COLOR_BUFFER_BIT) 222 w.glctxMu.Unlock() 223 224 w.Send(paint.Event{}) 225 }() 226 } 227 228 func eglErr() error { 229 if ret, _, _ := eglGetError.Call(); ret != _EGL_SUCCESS { 230 return errors.New(eglErrString(ret)) 231 } 232 return nil 233 } 234 235 func createEGLSurface(hwnd syscall.Handle, w *windowImpl) error { 236 var displayAttribPlatforms = [][]eglInt{ 237 // Default 238 []eglInt{ 239 _EGL_PLATFORM_ANGLE_TYPE_ANGLE, 240 _EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE, 241 _EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, _EGL_DONT_CARE, 242 _EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, _EGL_DONT_CARE, 243 _EGL_NONE, 244 }, 245 // Direct3D 11 246 []eglInt{ 247 _EGL_PLATFORM_ANGLE_TYPE_ANGLE, 248 _EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, 249 _EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, _EGL_DONT_CARE, 250 _EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, _EGL_DONT_CARE, 251 _EGL_NONE, 252 }, 253 // Direct3D 9 254 []eglInt{ 255 _EGL_PLATFORM_ANGLE_TYPE_ANGLE, 256 _EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE, 257 _EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, _EGL_DONT_CARE, 258 _EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, _EGL_DONT_CARE, 259 _EGL_NONE, 260 }, 261 // Direct3D 11 with WARP 262 // https://msdn.microsoft.com/en-us/library/windows/desktop/gg615082.aspx 263 []eglInt{ 264 _EGL_PLATFORM_ANGLE_TYPE_ANGLE, 265 _EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, 266 _EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, 267 _EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE, 268 _EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, _EGL_DONT_CARE, 269 _EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, _EGL_DONT_CARE, 270 _EGL_NONE, 271 }, 272 } 273 274 dc, err := win32.GetDC(hwnd) 275 if err != nil { 276 return fmt.Errorf("win32.GetDC failed: %v", err) 277 } 278 279 var display uintptr = _EGL_NO_DISPLAY 280 for i, displayAttrib := range displayAttribPlatforms { 281 lastTry := i == len(displayAttribPlatforms)-1 282 283 display, _, _ = eglGetPlatformDisplayEXT.Call( 284 _EGL_PLATFORM_ANGLE_ANGLE, 285 uintptr(dc), 286 uintptr(unsafe.Pointer(&displayAttrib[0])), 287 ) 288 289 if display == _EGL_NO_DISPLAY { 290 if !lastTry { 291 continue 292 } 293 return fmt.Errorf("eglGetPlatformDisplayEXT failed: %v", eglErr()) 294 } 295 296 if ret, _, _ := eglInitialize.Call(display, 0, 0); ret == 0 { 297 if !lastTry { 298 continue 299 } 300 return fmt.Errorf("eglInitialize failed: %v", eglErr()) 301 } 302 } 303 304 eglBindAPI.Call(_EGL_OPENGL_ES_API) 305 if err := eglErr(); err != nil { 306 return err 307 } 308 309 var numConfigs eglInt 310 var config eglConfig 311 ret, _, _ := eglChooseConfig.Call( 312 display, 313 uintptr(unsafe.Pointer(&rgb888[0])), 314 uintptr(unsafe.Pointer(&config)), 315 1, 316 uintptr(unsafe.Pointer(&numConfigs)), 317 ) 318 if ret == 0 { 319 return fmt.Errorf("eglChooseConfig failed: %v", eglErr()) 320 } 321 if numConfigs <= 0 { 322 return errors.New("eglChooseConfig found no valid config") 323 } 324 325 surface, _, _ := eglCreateWindowSurface.Call(display, uintptr(config), uintptr(hwnd), 0, 0) 326 if surface == _EGL_NO_SURFACE { 327 return fmt.Errorf("eglCreateWindowSurface failed: %v", eglErr()) 328 } 329 330 contextAttribs := [...]eglInt{ 331 _EGL_CONTEXT_CLIENT_VERSION, 2, 332 _EGL_NONE, 333 } 334 context, _, _ := eglCreateContext.Call( 335 display, 336 uintptr(config), 337 _EGL_NO_CONTEXT, 338 uintptr(unsafe.Pointer(&contextAttribs[0])), 339 ) 340 if context == _EGL_NO_CONTEXT { 341 return fmt.Errorf("eglCreateContext failed: %v", eglErr()) 342 } 343 344 eglSwapInterval.Call(display, 1) 345 346 w.ctx = ctxWin32{ 347 ctx: context, 348 display: display, 349 surface: surface, 350 } 351 352 return nil 353 } 354 355 func surfaceCreate() error { 356 return errors.New("gldriver: surface creation not implemented on windows") 357 }