forked from joejulian/gio
gpu/headless: move package app/headless
Package headless is more about rendering that windows. Move it accordingly. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package headless
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/png"
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"gioui.org/gpu/backend"
|
||||
"gioui.org/internal/f32color"
|
||||
"gioui.org/internal/unsafe"
|
||||
)
|
||||
|
||||
var dumpImages = flag.Bool("saveimages", false, "save test images")
|
||||
|
||||
var clearCol = color.NRGBA{A: 0xff, R: 0xde, G: 0xad, B: 0xbe}
|
||||
var clearColExpect = f32color.NRGBAToRGBA(clearCol)
|
||||
|
||||
func TestFramebufferClear(t *testing.T) {
|
||||
b := newBackend(t)
|
||||
sz := image.Point{X: 800, Y: 600}
|
||||
fbo := setupFBO(t, b, sz)
|
||||
img := screenshot(t, fbo, sz)
|
||||
if got := img.RGBAAt(0, 0); got != clearColExpect {
|
||||
t.Errorf("got color %v, expected %v", got, clearColExpect)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSimpleShader(t *testing.T) {
|
||||
b := newBackend(t)
|
||||
sz := image.Point{X: 800, Y: 600}
|
||||
fbo := setupFBO(t, b, sz)
|
||||
p, err := b.NewProgram(shader_simple_vert, shader_simple_frag)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer p.Release()
|
||||
b.BindProgram(p)
|
||||
b.DrawArrays(backend.DrawModeTriangles, 0, 3)
|
||||
img := screenshot(t, fbo, sz)
|
||||
if got := img.RGBAAt(0, 0); got != clearColExpect {
|
||||
t.Errorf("got color %v, expected %v", got, clearColExpect)
|
||||
}
|
||||
// Just off the center to catch inverted triangles.
|
||||
cx, cy := 300, 400
|
||||
shaderCol := f32color.RGBA{R: .25, G: .55, B: .75, A: 1.0}
|
||||
if got, exp := img.RGBAAt(cx, cy), shaderCol.SRGB(); got != f32color.NRGBAToRGBA(exp) {
|
||||
t.Errorf("got color %v, expected %v", got, f32color.NRGBAToRGBA(exp))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInputShader(t *testing.T) {
|
||||
b := newBackend(t)
|
||||
sz := image.Point{X: 800, Y: 600}
|
||||
fbo := setupFBO(t, b, sz)
|
||||
p, err := b.NewProgram(shader_input_vert, shader_simple_frag)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer p.Release()
|
||||
b.BindProgram(p)
|
||||
buf, err := b.NewImmutableBuffer(backend.BufferBindingVertices,
|
||||
unsafe.BytesView([]float32{
|
||||
0, .5, .5, 1,
|
||||
-.5, -.5, .5, 1,
|
||||
.5, -.5, .5, 1,
|
||||
}),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer buf.Release()
|
||||
b.BindVertexBuffer(buf, 4*4, 0)
|
||||
layout, err := b.NewInputLayout(shader_input_vert, []backend.InputDesc{
|
||||
{
|
||||
Type: backend.DataTypeFloat,
|
||||
Size: 4,
|
||||
Offset: 0,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer layout.Release()
|
||||
b.BindInputLayout(layout)
|
||||
b.DrawArrays(backend.DrawModeTriangles, 0, 3)
|
||||
img := screenshot(t, fbo, sz)
|
||||
if got := img.RGBAAt(0, 0); got != clearColExpect {
|
||||
t.Errorf("got color %v, expected %v", got, clearColExpect)
|
||||
}
|
||||
cx, cy := 300, 400
|
||||
shaderCol := f32color.RGBA{R: .25, G: .55, B: .75, A: 1.0}
|
||||
if got, exp := img.RGBAAt(cx, cy), shaderCol.SRGB(); got != f32color.NRGBAToRGBA(exp) {
|
||||
t.Errorf("got color %v, expected %v", got, f32color.NRGBAToRGBA(exp))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFramebuffers(t *testing.T) {
|
||||
b := newBackend(t)
|
||||
sz := image.Point{X: 800, Y: 600}
|
||||
fbo1 := newFBO(t, b, sz)
|
||||
fbo2 := newFBO(t, b, sz)
|
||||
var (
|
||||
col1 = color.NRGBA{R: 0xac, G: 0xbd, B: 0xef, A: 0xde}
|
||||
col2 = color.NRGBA{R: 0xfe, G: 0xba, B: 0xbe, A: 0xca}
|
||||
)
|
||||
fcol1, fcol2 := f32color.LinearFromSRGB(col1), f32color.LinearFromSRGB(col2)
|
||||
b.BindFramebuffer(fbo1)
|
||||
b.Clear(fcol1.Float32())
|
||||
b.BindFramebuffer(fbo2)
|
||||
b.Clear(fcol2.Float32())
|
||||
img := screenshot(t, fbo1, sz)
|
||||
if got := img.RGBAAt(0, 0); got != f32color.NRGBAToRGBA(col1) {
|
||||
t.Errorf("got color %v, expected %v", got, f32color.NRGBAToRGBA(col1))
|
||||
}
|
||||
img = screenshot(t, fbo2, sz)
|
||||
if got := img.RGBAAt(0, 0); got != f32color.NRGBAToRGBA(col2) {
|
||||
t.Errorf("got color %v, expected %v", got, f32color.NRGBAToRGBA(col2))
|
||||
}
|
||||
}
|
||||
|
||||
func setupFBO(t *testing.T, b backend.Device, size image.Point) backend.Framebuffer {
|
||||
fbo := newFBO(t, b, size)
|
||||
b.BindFramebuffer(fbo)
|
||||
// ClearColor accepts linear RGBA colors, while 8-bit colors
|
||||
// are in the sRGB color space.
|
||||
col := f32color.LinearFromSRGB(clearCol)
|
||||
b.Clear(col.Float32())
|
||||
b.ClearDepth(0.0)
|
||||
b.Viewport(0, 0, size.X, size.Y)
|
||||
return fbo
|
||||
}
|
||||
|
||||
func newFBO(t *testing.T, b backend.Device, size image.Point) backend.Framebuffer {
|
||||
fboTex, err := b.NewTexture(
|
||||
backend.TextureFormatSRGB,
|
||||
size.X, size.Y,
|
||||
backend.FilterNearest, backend.FilterNearest,
|
||||
backend.BufferBindingFramebuffer,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
fboTex.Release()
|
||||
})
|
||||
const depthBits = 16
|
||||
fbo, err := b.NewFramebuffer(fboTex, depthBits)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
fbo.Release()
|
||||
})
|
||||
return fbo
|
||||
}
|
||||
|
||||
func newBackend(t *testing.T) backend.Device {
|
||||
ctx, err := newContext()
|
||||
if err != nil {
|
||||
t.Skipf("no context available: %v", err)
|
||||
}
|
||||
runtime.LockOSThread()
|
||||
if err := ctx.MakeCurrent(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b, err := backend.NewDevice(ctx.API())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b.BeginFrame()
|
||||
t.Cleanup(func() {
|
||||
b.EndFrame()
|
||||
ctx.ReleaseCurrent()
|
||||
runtime.UnlockOSThread()
|
||||
ctx.Release()
|
||||
})
|
||||
return b
|
||||
}
|
||||
|
||||
func screenshot(t *testing.T, fbo backend.Framebuffer, size image.Point) *image.RGBA {
|
||||
img := image.NewRGBA(image.Rectangle{Max: size})
|
||||
err := fbo.ReadPixels(
|
||||
image.Rectangle{
|
||||
Max: image.Point{X: size.X, Y: size.Y},
|
||||
}, img.Pix)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if *dumpImages {
|
||||
if err := saveImage(t.Name()+".png", img); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
return img
|
||||
}
|
||||
|
||||
func saveImage(file string, img image.Image) error {
|
||||
var buf bytes.Buffer
|
||||
if err := png.Encode(&buf, img); err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(file, buf.Bytes(), 0666)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package headless
|
||||
|
||||
//go:generate go run ../../internal/cmd/convertshaders -package headless
|
||||
@@ -0,0 +1,148 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
// Package headless implements headless windows for rendering
|
||||
// an operation list to an image.
|
||||
package headless
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"runtime"
|
||||
|
||||
"gioui.org/gpu"
|
||||
"gioui.org/gpu/backend"
|
||||
"gioui.org/op"
|
||||
)
|
||||
|
||||
// Window is a headless window.
|
||||
type Window struct {
|
||||
size image.Point
|
||||
ctx context
|
||||
backend backend.Device
|
||||
gpu gpu.GPU
|
||||
fboTex backend.Texture
|
||||
fbo backend.Framebuffer
|
||||
}
|
||||
|
||||
type context interface {
|
||||
API() gpu.API
|
||||
MakeCurrent() error
|
||||
ReleaseCurrent()
|
||||
Release()
|
||||
}
|
||||
|
||||
// NewWindow creates a new headless window.
|
||||
func NewWindow(width, height int) (*Window, error) {
|
||||
ctx, err := newContext()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
w := &Window{
|
||||
size: image.Point{X: width, Y: height},
|
||||
ctx: ctx,
|
||||
}
|
||||
err = contextDo(ctx, func() error {
|
||||
api := ctx.API()
|
||||
dev, err := backend.NewDevice(api)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dev.Viewport(0, 0, width, height)
|
||||
fboTex, err := dev.NewTexture(
|
||||
backend.TextureFormatSRGB,
|
||||
width, height,
|
||||
backend.FilterNearest, backend.FilterNearest,
|
||||
backend.BufferBindingFramebuffer,
|
||||
)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
const depthBits = 16
|
||||
fbo, err := dev.NewFramebuffer(fboTex, depthBits)
|
||||
if err != nil {
|
||||
fboTex.Release()
|
||||
return err
|
||||
}
|
||||
gp, err := gpu.New(api)
|
||||
if err != nil {
|
||||
fbo.Release()
|
||||
fboTex.Release()
|
||||
return err
|
||||
}
|
||||
w.fboTex = fboTex
|
||||
w.fbo = fbo
|
||||
w.gpu = gp
|
||||
w.backend = dev
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
ctx.Release()
|
||||
return nil, err
|
||||
}
|
||||
return w, nil
|
||||
}
|
||||
|
||||
// Release resources associated with the window.
|
||||
func (w *Window) Release() {
|
||||
contextDo(w.ctx, func() error {
|
||||
if w.fbo != nil {
|
||||
w.fbo.Release()
|
||||
w.fbo = nil
|
||||
}
|
||||
if w.fboTex != nil {
|
||||
w.fboTex.Release()
|
||||
w.fboTex = nil
|
||||
}
|
||||
if w.gpu != nil {
|
||||
w.gpu.Release()
|
||||
w.gpu = nil
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if w.ctx != nil {
|
||||
w.ctx.Release()
|
||||
w.ctx = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Frame replace the window content and state with the
|
||||
// operation list.
|
||||
func (w *Window) Frame(frame *op.Ops) error {
|
||||
return contextDo(w.ctx, func() error {
|
||||
w.backend.BindFramebuffer(w.fbo)
|
||||
w.gpu.Clear(color.NRGBA{A: 0xff, R: 0xff, G: 0xff, B: 0xff})
|
||||
w.gpu.Collect(w.size, frame)
|
||||
return w.gpu.Frame()
|
||||
})
|
||||
}
|
||||
|
||||
// Screenshot returns an image with the content of the window.
|
||||
func (w *Window) Screenshot() (*image.RGBA, error) {
|
||||
img := image.NewRGBA(image.Rectangle{Max: w.size})
|
||||
err := contextDo(w.ctx, func() error {
|
||||
return w.fbo.ReadPixels(
|
||||
image.Rectangle{
|
||||
Max: image.Point{X: w.size.X, Y: w.size.Y},
|
||||
}, img.Pix)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return img, nil
|
||||
}
|
||||
|
||||
func contextDo(ctx context, f func() error) error {
|
||||
errCh := make(chan error)
|
||||
go func() {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
if err := ctx.MakeCurrent(); err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
err := f()
|
||||
ctx.ReleaseCurrent()
|
||||
errCh <- err
|
||||
}()
|
||||
return <-errCh
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package headless
|
||||
|
||||
import (
|
||||
"gioui.org/gpu"
|
||||
_ "gioui.org/internal/cocoainit"
|
||||
)
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -DGL_SILENCE_DEPRECATION -Werror -Wno-deprecated-declarations -fmodules -fobjc-arc -x objective-c
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_headless_newContext(void);
|
||||
__attribute__ ((visibility ("hidden"))) void gio_headless_releaseContext(CFTypeRef ctxRef);
|
||||
__attribute__ ((visibility ("hidden"))) void gio_headless_clearCurrentContext(CFTypeRef ctxRef);
|
||||
__attribute__ ((visibility ("hidden"))) void gio_headless_makeCurrentContext(CFTypeRef ctxRef);
|
||||
__attribute__ ((visibility ("hidden"))) void gio_headless_prepareContext(CFTypeRef ctxRef);
|
||||
*/
|
||||
import "C"
|
||||
|
||||
type nsContext struct {
|
||||
ctx C.CFTypeRef
|
||||
prepared bool
|
||||
}
|
||||
|
||||
func newGLContext() (context, error) {
|
||||
ctx := C.gio_headless_newContext()
|
||||
return &nsContext{ctx: ctx}, nil
|
||||
}
|
||||
|
||||
func (c *nsContext) API() gpu.API {
|
||||
return gpu.OpenGL{}
|
||||
}
|
||||
|
||||
func (c *nsContext) MakeCurrent() error {
|
||||
C.gio_headless_makeCurrentContext(c.ctx)
|
||||
if !c.prepared {
|
||||
C.gio_headless_prepareContext(c.ctx)
|
||||
c.prepared = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *nsContext) ReleaseCurrent() {
|
||||
C.gio_headless_clearCurrentContext(c.ctx)
|
||||
}
|
||||
|
||||
func (d *nsContext) Release() {
|
||||
if d.ctx != 0 {
|
||||
C.gio_headless_releaseContext(d.ctx)
|
||||
d.ctx = 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
// +build linux freebsd windows openbsd
|
||||
|
||||
package headless
|
||||
|
||||
import (
|
||||
"gioui.org/internal/egl"
|
||||
)
|
||||
|
||||
func newGLContext() (context, error) {
|
||||
return egl.NewContext(egl.EGL_DEFAULT_DISPLAY)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
// +build !windows
|
||||
|
||||
package headless
|
||||
|
||||
func newContext() (context, error) {
|
||||
return newGLContext()
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
// +build darwin,ios
|
||||
|
||||
@import OpenGLES;
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include "_cgo_export.h"
|
||||
|
||||
void gio_headless_releaseContext(CFTypeRef ctxRef) {
|
||||
CFBridgingRelease(ctxRef);
|
||||
}
|
||||
|
||||
CFTypeRef gio_headless_newContext(void) {
|
||||
EAGLContext *ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
|
||||
if (ctx == nil) {
|
||||
return nil;
|
||||
}
|
||||
return CFBridgingRetain(ctx);
|
||||
}
|
||||
|
||||
void gio_headless_clearCurrentContext(CFTypeRef ctxRef) {
|
||||
[EAGLContext setCurrentContext:nil];
|
||||
}
|
||||
|
||||
void gio_headless_makeCurrentContext(CFTypeRef ctxRef) {
|
||||
EAGLContext *ctx = (__bridge EAGLContext *)ctxRef;
|
||||
[EAGLContext setCurrentContext:ctx];
|
||||
}
|
||||
|
||||
void gio_headless_prepareContext(CFTypeRef ctxRef) {
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package headless
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall/js"
|
||||
|
||||
"gioui.org/gpu"
|
||||
"gioui.org/internal/glimpl"
|
||||
)
|
||||
|
||||
type jsContext struct {
|
||||
ctx js.Value
|
||||
}
|
||||
|
||||
func newGLContext() (context, error) {
|
||||
doc := js.Global().Get("document")
|
||||
cnv := doc.Call("createElement", "canvas")
|
||||
ctx := cnv.Call("getContext", "webgl2")
|
||||
if ctx.IsNull() {
|
||||
ctx = cnv.Call("getContext", "webgl")
|
||||
}
|
||||
if ctx.IsNull() {
|
||||
return nil, errors.New("headless: webgl is not supported")
|
||||
}
|
||||
c := &jsContext{
|
||||
ctx: ctx,
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *jsContext) API() gpu.API {
|
||||
return gpu.OpenGL{Context: glimpl.Context(c.ctx)}
|
||||
}
|
||||
|
||||
func (c *jsContext) Release() {
|
||||
}
|
||||
|
||||
func (c *jsContext) ReleaseCurrent() {
|
||||
}
|
||||
|
||||
func (c *jsContext) MakeCurrent() error {
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
// +build darwin,!ios
|
||||
|
||||
@import AppKit;
|
||||
@import OpenGL;
|
||||
@import OpenGL.GL;
|
||||
@import OpenGL.GL3;
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include "_cgo_export.h"
|
||||
|
||||
void gio_headless_releaseContext(CFTypeRef ctxRef) {
|
||||
CFBridgingRelease(ctxRef);
|
||||
}
|
||||
|
||||
CFTypeRef gio_headless_newContext(void) {
|
||||
NSOpenGLPixelFormatAttribute attr[] = {
|
||||
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
|
||||
NSOpenGLPFAColorSize, 24,
|
||||
NSOpenGLPFAAccelerated,
|
||||
// Opt-in to automatic GPU switching. CGL-only property.
|
||||
kCGLPFASupportsAutomaticGraphicsSwitching,
|
||||
NSOpenGLPFAAllowOfflineRenderers,
|
||||
0
|
||||
};
|
||||
NSOpenGLPixelFormat *pixFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr];
|
||||
if (pixFormat == nil) {
|
||||
return NULL;
|
||||
}
|
||||
NSOpenGLContext *ctx = [[NSOpenGLContext alloc] initWithFormat:pixFormat shareContext:nil];
|
||||
return CFBridgingRetain(ctx);
|
||||
}
|
||||
|
||||
void gio_headless_clearCurrentContext(CFTypeRef ctxRef) {
|
||||
NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef;
|
||||
CGLUnlockContext([ctx CGLContextObj]);
|
||||
[NSOpenGLContext clearCurrentContext];
|
||||
}
|
||||
|
||||
void gio_headless_makeCurrentContext(CFTypeRef ctxRef) {
|
||||
NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef;
|
||||
[ctx makeCurrentContext];
|
||||
CGLLockContext([ctx CGLContextObj]);
|
||||
}
|
||||
|
||||
void gio_headless_prepareContext(CFTypeRef ctxRef) {
|
||||
// Bind a default VBA to emulate OpenGL ES 2.
|
||||
GLuint defVBA;
|
||||
glGenVertexArrays(1, &defVBA);
|
||||
glBindVertexArray(defVBA);
|
||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package headless
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"testing"
|
||||
|
||||
"gioui.org/f32"
|
||||
"gioui.org/internal/f32color"
|
||||
"gioui.org/op"
|
||||
"gioui.org/op/clip"
|
||||
"gioui.org/op/paint"
|
||||
)
|
||||
|
||||
func TestHeadless(t *testing.T) {
|
||||
w, release := newTestWindow(t)
|
||||
defer release()
|
||||
|
||||
sz := w.size
|
||||
col := color.NRGBA{A: 0xff, R: 0xca, G: 0xfe}
|
||||
var ops op.Ops
|
||||
paint.ColorOp{Color: col}.Add(&ops)
|
||||
// Paint only part of the screen to avoid the glClear optimization.
|
||||
paint.FillShape(&ops, col, clip.Rect(image.Rect(0, 0, sz.X-100, sz.Y-100)).Op())
|
||||
if err := w.Frame(&ops); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
img, err := w.Screenshot()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if isz := img.Bounds().Size(); isz != sz {
|
||||
t.Errorf("got %v screenshot, expected %v", isz, sz)
|
||||
}
|
||||
if got := img.RGBAAt(0, 0); got != f32color.NRGBAToRGBA(col) {
|
||||
t.Errorf("got color %v, expected %v", got, f32color.NRGBAToRGBA(col))
|
||||
}
|
||||
}
|
||||
|
||||
func TestClipping(t *testing.T) {
|
||||
w, release := newTestWindow(t)
|
||||
defer release()
|
||||
|
||||
col := color.NRGBA{A: 0xff, R: 0xca, G: 0xfe}
|
||||
col2 := color.NRGBA{A: 0xff, R: 0x00, G: 0xfe}
|
||||
var ops op.Ops
|
||||
paint.ColorOp{Color: col}.Add(&ops)
|
||||
clip.RRect{
|
||||
Rect: f32.Rectangle{
|
||||
Min: f32.Point{X: 50, Y: 50},
|
||||
Max: f32.Point{X: 250, Y: 250},
|
||||
},
|
||||
SE: 75,
|
||||
}.Add(&ops)
|
||||
paint.PaintOp{}.Add(&ops)
|
||||
paint.ColorOp{Color: col2}.Add(&ops)
|
||||
clip.RRect{
|
||||
Rect: f32.Rectangle{
|
||||
Min: f32.Point{X: 100, Y: 100},
|
||||
Max: f32.Point{X: 350, Y: 350},
|
||||
},
|
||||
NW: 75,
|
||||
}.Add(&ops)
|
||||
paint.PaintOp{}.Add(&ops)
|
||||
if err := w.Frame(&ops); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
img, err := w.Screenshot()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if *dumpImages {
|
||||
if err := saveImage("clip.png", img); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
bg := color.NRGBA{A: 0xff, R: 0xff, G: 0xff, B: 0xff}
|
||||
tests := []struct {
|
||||
x, y int
|
||||
color color.NRGBA
|
||||
}{
|
||||
{120, 120, col},
|
||||
{130, 130, col2},
|
||||
{210, 210, col2},
|
||||
{230, 230, bg},
|
||||
}
|
||||
for _, test := range tests {
|
||||
if got := img.RGBAAt(test.x, test.y); got != f32color.NRGBAToRGBA(test.color) {
|
||||
t.Errorf("(%d,%d): got color %v, expected %v", test.x, test.y, got, f32color.NRGBAToRGBA(test.color))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDepth(t *testing.T) {
|
||||
w, release := newTestWindow(t)
|
||||
defer release()
|
||||
var ops op.Ops
|
||||
|
||||
blue := color.NRGBA{B: 0xFF, A: 0xFF}
|
||||
paint.FillShape(&ops, blue, clip.Rect(image.Rect(0, 0, 50, 100)).Op())
|
||||
red := color.NRGBA{R: 0xFF, A: 0xFF}
|
||||
paint.FillShape(&ops, red, clip.Rect(image.Rect(0, 0, 100, 50)).Op())
|
||||
if err := w.Frame(&ops); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
img, err := w.Screenshot()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if *dumpImages {
|
||||
if err := saveImage("depth.png", img); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
tests := []struct {
|
||||
x, y int
|
||||
color color.NRGBA
|
||||
}{
|
||||
{25, 25, red},
|
||||
{75, 25, red},
|
||||
{25, 75, blue},
|
||||
}
|
||||
for _, test := range tests {
|
||||
if got := img.RGBAAt(test.x, test.y); got != f32color.NRGBAToRGBA(test.color) {
|
||||
t.Errorf("(%d,%d): got color %v, expected %v", test.x, test.y, got, f32color.NRGBAToRGBA(test.color))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newTestWindow(t *testing.T) (*Window, func()) {
|
||||
t.Helper()
|
||||
sz := image.Point{X: 800, Y: 600}
|
||||
w, err := NewWindow(sz.X, sz.Y)
|
||||
if err != nil {
|
||||
t.Skipf("headless windows not supported: %v", err)
|
||||
}
|
||||
return w, func() {
|
||||
w.Release()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package headless
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"gioui.org/gpu"
|
||||
"gioui.org/internal/d3d11"
|
||||
)
|
||||
|
||||
type d3d11Context struct {
|
||||
dev *d3d11.Device
|
||||
}
|
||||
|
||||
func newContext() (context, error) {
|
||||
dev, ctx, _, err := d3d11.CreateDevice(
|
||||
d3d11.DRIVER_TYPE_HARDWARE,
|
||||
0,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Don't need it.
|
||||
d3d11.IUnknownRelease(unsafe.Pointer(ctx), ctx.Vtbl.Release)
|
||||
return &d3d11Context{dev: dev}, nil
|
||||
}
|
||||
|
||||
func (c *d3d11Context) API() gpu.API {
|
||||
return gpu.Direct3D11{Device: unsafe.Pointer(c.dev)}
|
||||
}
|
||||
|
||||
func (c *d3d11Context) MakeCurrent() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *d3d11Context) ReleaseCurrent() {
|
||||
}
|
||||
|
||||
func (c *d3d11Context) Release() {
|
||||
d3d11.IUnknownRelease(unsafe.Pointer(c.dev), c.dev.Vtbl.Release)
|
||||
c.dev = nil
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Code generated by build.go. DO NOT EDIT.
|
||||
|
||||
package headless
|
||||
|
||||
import "gioui.org/gpu/backend"
|
||||
|
||||
var (
|
||||
shader_input_vert = backend.ShaderSources{
|
||||
Name: "input.vert",
|
||||
Inputs: []backend.InputLocation{{Name: "position", Location: 0, Semantic: "POSITION", SemanticIndex: 0, Type: 0x0, Size: 4}},
|
||||
GLSL100ES: "\nattribute vec4 position;\n\nvoid main()\n{\n gl_Position = position;\n}\n\n",
|
||||
GLSL300ES: "#version 300 es\n\nlayout(location = 0) in vec4 position;\n\nvoid main()\n{\n gl_Position = position;\n}\n\n",
|
||||
GLSL130: "#version 130\n\nin vec4 position;\n\nvoid main()\n{\n gl_Position = position;\n}\n\n",
|
||||
GLSL150: "#version 150\n\nin vec4 position;\n\nvoid main()\n{\n gl_Position = position;\n}\n\n",
|
||||
HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0x35, 0xe9, 0xae, 0x29, 0x96, 0x7e, 0x7c, 0xe6, 0x40, 0xb0, 0x4e, 0x29, 0xd9, 0x98, 0x51, 0x7c, 0x1, 0x0, 0x0, 0x0, 0x10, 0x2, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x9c, 0x0, 0x0, 0x0, 0xe0, 0x0, 0x0, 0x0, 0x5c, 0x1, 0x0, 0x0, 0xa8, 0x1, 0x0, 0x0, 0xdc, 0x1, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0x5c, 0x0, 0x0, 0x0, 0x5c, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0x34, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x24, 0x0, 0x1, 0x0, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfe, 0xff, 0x1f, 0x0, 0x0, 0x2, 0x5, 0x0, 0x0, 0x80, 0x0, 0x0, 0xf, 0x90, 0x4, 0x0, 0x0, 0x4, 0x0, 0x0, 0x3, 0xc0, 0x0, 0x0, 0xff, 0x90, 0x0, 0x0, 0xe4, 0xa0, 0x0, 0x0, 0xe4, 0x90, 0x1, 0x0, 0x0, 0x2, 0x0, 0x0, 0xc, 0xc0, 0x0, 0x0, 0xe4, 0x90, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0x3c, 0x0, 0x0, 0x0, 0x40, 0x0, 0x1, 0x0, 0xf, 0x0, 0x0, 0x0, 0x5f, 0x0, 0x0, 0x3, 0xf2, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x67, 0x0, 0x0, 0x4, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x5, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x1e, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0x44, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xfe, 0xff, 0x0, 0x1, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0x49, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0xf, 0x0, 0x0, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x0, 0xab, 0xab, 0xab, 0x4f, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x53, 0x56, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x0},
|
||||
}
|
||||
shader_simple_frag = backend.ShaderSources{
|
||||
Name: "simple.frag",
|
||||
GLSL100ES: "precision mediump float;\nprecision highp int;\n\nvoid main()\n{\n gl_FragData[0] = vec4(0.25, 0.550000011920928955078125, 0.75, 1.0);\n}\n\n",
|
||||
GLSL300ES: "#version 300 es\nprecision mediump float;\nprecision highp int;\n\nlayout(location = 0) out vec4 fragColor;\n\nvoid main()\n{\n fragColor = vec4(0.25, 0.550000011920928955078125, 0.75, 1.0);\n}\n\n",
|
||||
GLSL130: "#version 130\n\nout vec4 fragColor;\n\nvoid main()\n{\n fragColor = vec4(0.25, 0.550000011920928955078125, 0.75, 1.0);\n}\n\n",
|
||||
GLSL150: "#version 150\n\nout vec4 fragColor;\n\nvoid main()\n{\n fragColor = vec4(0.25, 0.550000011920928955078125, 0.75, 1.0);\n}\n\n",
|
||||
HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0xf5, 0x46, 0xde, 0x66, 0x24, 0x29, 0xa8, 0xbb, 0x56, 0xea, 0x73, 0xb5, 0x6b, 0x73, 0x12, 0x72, 0x1, 0x0, 0x0, 0x0, 0xdc, 0x1, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x90, 0x0, 0x0, 0x0, 0xd0, 0x0, 0x0, 0x0, 0x4c, 0x1, 0x0, 0x0, 0x98, 0x1, 0x0, 0x0, 0xa8, 0x1, 0x0, 0x0, 0x41, 0x6f, 0x6e, 0x39, 0x50, 0x0, 0x0, 0x0, 0x50, 0x0, 0x0, 0x0, 0x0, 0x2, 0xff, 0xff, 0x2c, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x0, 0x24, 0x0, 0x0, 0x2, 0xff, 0xff, 0x51, 0x0, 0x0, 0x5, 0x0, 0x0, 0xf, 0xa0, 0x0, 0x0, 0x80, 0x3e, 0xcd, 0xcc, 0xc, 0x3f, 0x0, 0x0, 0x40, 0x3f, 0x0, 0x0, 0x80, 0x3f, 0x1, 0x0, 0x0, 0x2, 0x0, 0x8, 0xf, 0x80, 0x0, 0x0, 0xe4, 0xa0, 0xff, 0xff, 0x0, 0x0, 0x53, 0x48, 0x44, 0x52, 0x38, 0x0, 0x0, 0x0, 0x40, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x3, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x8, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x80, 0x3e, 0xcd, 0xcc, 0xc, 0x3f, 0x0, 0x0, 0x40, 0x3f, 0x0, 0x0, 0x80, 0x3f, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0x44, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xff, 0xff, 0x0, 0x1, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0x49, 0x53, 0x47, 0x4e, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x4f, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x0, 0xab, 0xab},
|
||||
}
|
||||
shader_simple_vert = backend.ShaderSources{
|
||||
Name: "simple.vert",
|
||||
GLSL100ES: "\nvoid main()\n{\n float x;\n float y;\n if (gl_VertexID == 0)\n {\n x = 0.0;\n y = 0.5;\n }\n else\n {\n if (gl_VertexID == 1)\n {\n x = 0.5;\n y = -0.5;\n }\n else\n {\n x = -0.5;\n y = -0.5;\n }\n }\n gl_Position = vec4(x, y, 0.5, 1.0);\n}\n\n",
|
||||
GLSL300ES: "#version 300 es\n\nvoid main()\n{\n float x;\n float y;\n if (gl_VertexID == 0)\n {\n x = 0.0;\n y = 0.5;\n }\n else\n {\n if (gl_VertexID == 1)\n {\n x = 0.5;\n y = -0.5;\n }\n else\n {\n x = -0.5;\n y = -0.5;\n }\n }\n gl_Position = vec4(x, y, 0.5, 1.0);\n}\n\n",
|
||||
GLSL130: "#version 130\n\nvoid main()\n{\n float x;\n float y;\n if (gl_VertexID == 0)\n {\n x = 0.0;\n y = 0.5;\n }\n else\n {\n if (gl_VertexID == 1)\n {\n x = 0.5;\n y = -0.5;\n }\n else\n {\n x = -0.5;\n y = -0.5;\n }\n }\n gl_Position = vec4(x, y, 0.5, 1.0);\n}\n\n",
|
||||
GLSL150: "#version 150\n\nvoid main()\n{\n float x;\n float y;\n if (gl_VertexID == 0)\n {\n x = 0.0;\n y = 0.5;\n }\n else\n {\n if (gl_VertexID == 1)\n {\n x = 0.5;\n y = -0.5;\n }\n else\n {\n x = -0.5;\n y = -0.5;\n }\n }\n gl_Position = vec4(x, y, 0.5, 1.0);\n}\n\n",
|
||||
HLSL: []byte{0x44, 0x58, 0x42, 0x43, 0xc8, 0x20, 0x5c, 0x22, 0xec, 0xe9, 0xb2, 0x29, 0x40, 0xdf, 0x7c, 0x5a, 0x28, 0xea, 0xc, 0xb8, 0x1, 0x0, 0x0, 0x0, 0x48, 0x2, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0xb4, 0x0, 0x0, 0x0, 0xe8, 0x0, 0x0, 0x0, 0xcc, 0x1, 0x0, 0x0, 0x52, 0x44, 0x45, 0x46, 0x44, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x0, 0x4, 0xfe, 0xff, 0x0, 0x1, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x28, 0x52, 0x29, 0x20, 0x48, 0x4c, 0x53, 0x4c, 0x20, 0x53, 0x68, 0x61, 0x64, 0x65, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x0, 0x49, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x53, 0x56, 0x5f, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x49, 0x44, 0x0, 0x4f, 0x53, 0x47, 0x4e, 0x2c, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0x53, 0x56, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x0, 0x53, 0x48, 0x44, 0x52, 0xdc, 0x0, 0x0, 0x0, 0x40, 0x0, 0x1, 0x0, 0x37, 0x0, 0x0, 0x0, 0x60, 0x0, 0x0, 0x4, 0x12, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x67, 0x0, 0x0, 0x4, 0xf2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x68, 0x0, 0x0, 0x2, 0x1, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x7, 0x12, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x40, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x37, 0x0, 0x0, 0xf, 0x32, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0xbf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0xbf, 0x0, 0x0, 0x0, 0xbf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x37, 0x0, 0x0, 0xc, 0x32, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x10, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0x0, 0x0, 0x8, 0xc2, 0x20, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x80, 0x3f, 0x3e, 0x0, 0x0, 0x1, 0x53, 0x54, 0x41, 0x54, 0x74, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
#version 310 es
|
||||
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
precision highp float;
|
||||
|
||||
layout(location=0) in vec4 position;
|
||||
|
||||
void main() {
|
||||
gl_Position = position;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#version 310 es
|
||||
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
precision mediump float;
|
||||
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
fragColor = vec4(.25, .55, .75, 1.0);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#version 310 es
|
||||
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
precision highp float;
|
||||
|
||||
void main() {
|
||||
float x, y;
|
||||
if (gl_VertexIndex == 0) {
|
||||
x = 0.0;
|
||||
y = .5;
|
||||
} else if (gl_VertexIndex == 1) {
|
||||
x = .5;
|
||||
y = -.5;
|
||||
} else {
|
||||
x = -.5;
|
||||
y = -.5;
|
||||
}
|
||||
gl_Position = vec4(x, y, 0.5, 1.0);
|
||||
}
|
||||
Reference in New Issue
Block a user