x/mobile/gl: fix windows context3 and dlls

Add missing context3 for Windows.
Add missing d3dcompiler_47.dll loading.
Check whether dll architecture matches.
Search ANGLE from Chrome path.

Fixes golang/go#16991

Change-Id: Ia042f75241c2398fabda03bb2d0e683eb34545c7
Reviewed-on: https://go-review.googlesource.com/28814
Reviewed-by: David Crawshaw <crawshaw@golang.org>
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
diff --git a/gl/dll_windows.go b/gl/dll_windows.go
index 50e6257..1afc5f8 100644
--- a/gl/dll_windows.go
+++ b/gl/dll_windows.go
@@ -7,6 +7,7 @@
 import (
 	"archive/tar"
 	"compress/gzip"
+	"debug/pe"
 	"fmt"
 	"io"
 	"io/ioutil"
@@ -20,7 +21,7 @@
 var debug = log.New(ioutil.Discard, "gl: ", log.LstdFlags)
 
 func downloadDLLs() (path string, err error) {
-	url := "https://dl.google.com/go/mobile/angle-dd5c5b-" + runtime.GOARCH + ".tgz"
+	url := "https://dl.google.com/go/mobile/angle-bd3f8780b-" + runtime.GOARCH + ".tgz"
 	debug.Printf("downloading %s", url)
 	resp, err := http.Get(url)
 	if err != nil {
@@ -42,7 +43,7 @@
 		return "", fmt.Errorf("gl: error reading gzip from %v: %v", url, err)
 	}
 	tr := tar.NewReader(r)
-	var bytesGLESv2, bytesEGL []byte
+	var bytesGLESv2, bytesEGL, bytesD3DCompiler []byte
 	for {
 		header, err := tr.Next()
 		if err == io.EOF {
@@ -56,14 +57,16 @@
 			bytesGLESv2, err = ioutil.ReadAll(tr)
 		case "angle-" + runtime.GOARCH + "/libegl.dll":
 			bytesEGL, err = ioutil.ReadAll(tr)
+		case "angle-" + runtime.GOARCH + "/d3dcompiler_47.dll":
+			bytesD3DCompiler, err = ioutil.ReadAll(tr)
 		default: // skip
 		}
 		if err != nil {
 			return "", fmt.Errorf("gl: error reading %v from %v: %v", header.Name, url, err)
 		}
 	}
-	if len(bytesGLESv2) == 0 || len(bytesEGL) == 0 {
-		return "", fmt.Errorf("gl: did not find both DLLs in %v", url)
+	if len(bytesGLESv2) == 0 || len(bytesEGL) == 0 || len(bytesD3DCompiler) == 0 {
+		return "", fmt.Errorf("gl: did not find all DLLs in %v", url)
 	}
 
 	writeDLLs := func(path string) error {
@@ -73,6 +76,9 @@
 		if err := ioutil.WriteFile(filepath.Join(path, "libegl.dll"), bytesEGL, 0755); err != nil {
 			return fmt.Errorf("gl: cannot install ANGLE: %v", err)
 		}
+		if err := ioutil.WriteFile(filepath.Join(path, "d3dcompiler_47.dll"), bytesD3DCompiler, 0755); err != nil {
+			return fmt.Errorf("gl: cannot install ANGLE: %v", err)
+		}
 		return nil
 	}
 
@@ -108,16 +114,84 @@
 	return filepath.Join(os.Getenv("LOCALAPPDATA"), "GoGL", runtime.GOARCH)
 }
 
+func containsDLLs(dir string) bool {
+	compatible := func(name string) bool {
+		file, err := pe.Open(filepath.Join(dir, name))
+		if err != nil {
+			return false
+		}
+		defer file.Close()
+
+		switch file.Machine {
+		case pe.IMAGE_FILE_MACHINE_AMD64:
+			return "amd64" == runtime.GOARCH
+		case pe.IMAGE_FILE_MACHINE_ARM:
+			return "arm" == runtime.GOARCH
+		case pe.IMAGE_FILE_MACHINE_I386:
+			return "386" == runtime.GOARCH
+		}
+		return false
+	}
+
+	return compatible("libglesv2.dll") && compatible("libegl.dll") && compatible("d3dcompiler_47.dll")
+}
+
+func chromePath() string {
+	// dlls are stored in:
+	//   <BASE>/<VERSION>/libglesv2.dll
+
+	var installdirs = []string{
+		// Chrome User
+		filepath.Join(os.Getenv("LOCALAPPDATA"), "Google", "Chrome", "Application"),
+		// Chrome System
+		filepath.Join(os.Getenv("ProgramFiles(x86)"), "Google", "Chrome", "Application"),
+		// Chromium
+		filepath.Join(os.Getenv("LOCALAPPDATA"), "Chromium", "Application"),
+		// Chrome Canary
+		filepath.Join(os.Getenv("LOCALAPPDATA"), "Google", "Chrome SxS", "Application"),
+	}
+
+	for _, installdir := range installdirs {
+		versiondirs, err := ioutil.ReadDir(installdir)
+		if err != nil {
+			continue
+		}
+
+		for _, versiondir := range versiondirs {
+			if !versiondir.IsDir() {
+				continue
+			}
+
+			versionpath := filepath.Join(installdir, versiondir.Name())
+			if containsDLLs(versionpath) {
+				return versionpath
+			}
+		}
+	}
+
+	return ""
+}
+
 func findDLLs() (err error) {
 	load := func(path string) (bool, error) {
 		if path != "" {
+			// don't try to start when one of the files is missing
+			if !containsDLLs(path) {
+				return false, nil
+			}
+
+			LibD3DCompiler.Name = filepath.Join(path, filepath.Base(LibD3DCompiler.Name))
 			LibGLESv2.Name = filepath.Join(path, filepath.Base(LibGLESv2.Name))
 			LibEGL.Name = filepath.Join(path, filepath.Base(LibEGL.Name))
 		}
+
 		if err := LibGLESv2.Load(); err == nil {
 			if err := LibEGL.Load(); err != nil {
 				return false, fmt.Errorf("gl: loaded libglesv2 but not libegl: %v", err)
 			}
+			if err := LibD3DCompiler.Load(); err != nil {
+				return false, fmt.Errorf("gl: loaded libglesv2, libegl but not d3dcompiler: %v", err)
+			}
 			if path == "" {
 				debug.Printf("DLLs found")
 			} else {
@@ -125,6 +199,7 @@
 			}
 			return true, nil
 		}
+
 		return false, nil
 	}
 
@@ -138,7 +213,12 @@
 		return err
 	}
 
-	// TODO: Look for a Chrome installation.
+	// Look for a Chrome installation
+	if dir := chromePath(); dir != "" {
+		if ok, err := load(dir); ok || err != nil {
+			return err
+		}
+	}
 
 	// Look in GOPATH/pkg.
 	if ok, err := load(filepath.Join(os.Getenv("GOPATH"), "pkg")); ok || err != nil {
diff --git a/gl/work_windows.go b/gl/work_windows.go
index 53c434e..c2bd09d 100644
--- a/gl/work_windows.go
+++ b/gl/work_windows.go
@@ -23,6 +23,10 @@
 
 func (ctx *context) WorkAvailable() <-chan struct{} { return ctx.workAvailable }
 
+type context3 struct {
+	*context
+}
+
 func NewContext() (Context, Worker) {
 	if err := findDLLs(); err != nil {
 		panic(err)
@@ -400,9 +404,12 @@
 //
 // LibEGL is not used directly by the gl package, but is needed by any
 // driver hoping to use OpenGL ES.
+//
+// LibD3DCompiler is needed by libglesv2.dll for compiling shaders.
 var (
-	LibGLESv2 = syscall.NewLazyDLL("libglesv2.dll")
-	LibEGL    = syscall.NewLazyDLL("libegl.dll")
+	LibGLESv2      = syscall.NewLazyDLL("libglesv2.dll")
+	LibEGL         = syscall.NewLazyDLL("libegl.dll")
+	LibD3DCompiler = syscall.NewLazyDLL("d3dcompiler_47.dll")
 )
 
 var (