mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-06 09:55:40 +00:00
app/headless: add lower-level backend tests
Add a series of low level gpu.Backend tests to assure the correct behaviour of Backends. The immediate use is debugging of the Direct3D port, in the future for developing new backends. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
@@ -0,0 +1,247 @@
|
|||||||
|
// SPDX-License-Identifier: Unlicense OR MIT
|
||||||
|
|
||||||
|
package headless
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"flag"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/png"
|
||||||
|
"io/ioutil"
|
||||||
|
"math"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gioui.org/gpu/backend"
|
||||||
|
"gioui.org/internal/unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var dumpImages = flag.Bool("saveimages", false, "save test images")
|
||||||
|
|
||||||
|
var clearCol = color.RGBA{A: 0xff, R: 0xde, G: 0xad, B: 0xbe}
|
||||||
|
|
||||||
|
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 != clearCol {
|
||||||
|
t.Errorf("got color %v, expected %v", got, clearCol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 != clearCol {
|
||||||
|
t.Errorf("got color %v, expected %v", got, clearCol)
|
||||||
|
}
|
||||||
|
// Just off the center to catch inverted triangles.
|
||||||
|
cx, cy := 300, 400
|
||||||
|
shaderCol := [4]float32{.25, .55, .75, 1.0}
|
||||||
|
if got, exp := img.RGBAAt(cx, cy), tosRGB(shaderCol); got != exp {
|
||||||
|
t.Errorf("got color %v, expected %v", got, 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 != clearCol {
|
||||||
|
t.Errorf("got color %v, expected %v", got, clearCol)
|
||||||
|
}
|
||||||
|
cx, cy := 300, 400
|
||||||
|
shaderCol := [4]float32{.25, .55, .75, 1.0}
|
||||||
|
if got, exp := img.RGBAAt(cx, cy), tosRGB(shaderCol); got != exp {
|
||||||
|
t.Errorf("got color %v, expected %v", got, 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.RGBA{R: 0xad, G: 0xbe, B: 0xef, A: 0xde}
|
||||||
|
col2 = color.RGBA{R: 0xfe, G: 0xba, B: 0xbe, A: 0xca}
|
||||||
|
)
|
||||||
|
fcol1, fcol2 := fromsRGB(col1), fromsRGB(col2)
|
||||||
|
b.ClearColor(fcol1[0], fcol1[1], fcol1[2], fcol1[3])
|
||||||
|
b.BindFramebuffer(fbo1)
|
||||||
|
b.Clear(backend.BufferAttachmentColor)
|
||||||
|
b.ClearColor(fcol2[0], fcol2[1], fcol2[2], fcol2[3])
|
||||||
|
b.BindFramebuffer(fbo2)
|
||||||
|
b.Clear(backend.BufferAttachmentColor)
|
||||||
|
img := screenshot(t, fbo1, sz)
|
||||||
|
if got := img.RGBAAt(0, 0); got != col1 {
|
||||||
|
t.Errorf("got color %v, expected %v", got, col1)
|
||||||
|
}
|
||||||
|
img = screenshot(t, fbo2, sz)
|
||||||
|
if got := img.RGBAAt(0, 0); got != col2 {
|
||||||
|
t.Errorf("got color %v, expected %v", got, col2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupFBO(t *testing.T, b backend.Device, size image.Point) backend.Framebuffer {
|
||||||
|
fbo := newFBO(t, b, size)
|
||||||
|
b.BindFramebuffer(fbo)
|
||||||
|
b.Clear(backend.BufferAttachmentColor | backend.BufferAttachmentDepth)
|
||||||
|
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 := ctx.Backend()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
b.BeginFrame()
|
||||||
|
// ClearColor accepts linear RGBA colors, while 8-bit colors
|
||||||
|
// are in the sRGB color space.
|
||||||
|
col := fromsRGB(clearCol)
|
||||||
|
b.ClearColor(col[0], col[1], col[2], col[3])
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
flipImageY(img)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func tosRGB(col [4]float32) color.RGBA {
|
||||||
|
for i := 0; i <= 2; i++ {
|
||||||
|
c := col[i]
|
||||||
|
// Use the formula from EXT_sRGB.
|
||||||
|
switch {
|
||||||
|
case c <= 0:
|
||||||
|
c = 0
|
||||||
|
case 0 < c && c < 0.0031308:
|
||||||
|
c = 12.92 * c
|
||||||
|
case 0.0031308 <= c && c < 1:
|
||||||
|
c = 1.055*float32(math.Pow(float64(c), 0.41666)) - 0.055
|
||||||
|
case c >= 1:
|
||||||
|
c = 1
|
||||||
|
}
|
||||||
|
col[i] = c
|
||||||
|
}
|
||||||
|
return color.RGBA{R: uint8(col[0]*255 + .5), G: uint8(col[1]*255 + .5), B: uint8(col[2]*255 + .5), A: uint8(col[3]*255 + .5)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fromsRGB(col color.Color) [4]float32 {
|
||||||
|
r, g, b, a := col.RGBA()
|
||||||
|
color := [4]float32{float32(r) / 0xffff, float32(g) / 0xffff, float32(b) / 0xffff, float32(a) / 0xffff}
|
||||||
|
for i := 0; i <= 2; i++ {
|
||||||
|
c := color[i]
|
||||||
|
// Use the formula from EXT_sRGB.
|
||||||
|
if c <= 0.04045 {
|
||||||
|
c = c / 12.92
|
||||||
|
} else {
|
||||||
|
c = float32(math.Pow(float64((c+0.055)/1.055), 2.4))
|
||||||
|
}
|
||||||
|
color[i] = c
|
||||||
|
}
|
||||||
|
return color
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// SPDX-License-Identifier: Unlicense OR MIT
|
||||||
|
|
||||||
|
package headless
|
||||||
|
|
||||||
|
//go:generate go run ../../internal/cmd/convertshaders -package headless
|
||||||
@@ -127,18 +127,23 @@ func (w *Window) Screenshot() (*image.RGBA, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
flipImageY(img)
|
||||||
|
return img, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func flipImageY(img *image.RGBA) {
|
||||||
// Flip image in y-direction. OpenGL's origin is in the lower
|
// Flip image in y-direction. OpenGL's origin is in the lower
|
||||||
// left corner.
|
// left corner.
|
||||||
row := make([]uint8, img.Stride)
|
row := make([]uint8, img.Stride)
|
||||||
for y := 0; y < w.size.Y/2; y++ {
|
sy := img.Bounds().Dy()
|
||||||
y1 := w.size.Y - y - 1
|
for y := 0; y < sy/2; y++ {
|
||||||
|
y1 := sy - y - 1
|
||||||
dest := img.PixOffset(0, y1)
|
dest := img.PixOffset(0, y1)
|
||||||
src := img.PixOffset(0, y)
|
src := img.PixOffset(0, y)
|
||||||
copy(row, img.Pix[dest:])
|
copy(row, img.Pix[dest:])
|
||||||
copy(img.Pix[dest:], img.Pix[src:src+len(row)])
|
copy(img.Pix[dest:], img.Pix[src:src+len(row)])
|
||||||
copy(img.Pix[src:], row)
|
copy(img.Pix[src:], row)
|
||||||
}
|
}
|
||||||
return img, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func contextDo(ctx context, f func() error) error {
|
func contextDo(ctx context, f func() error) error {
|
||||||
|
|||||||
@@ -13,14 +13,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestHeadless(t *testing.T) {
|
func TestHeadless(t *testing.T) {
|
||||||
sz := image.Point{X: 800, Y: 600}
|
w, release := newTestWindow(t)
|
||||||
w, err := NewWindow(sz.X, sz.Y)
|
defer release()
|
||||||
if err != nil {
|
|
||||||
t.Skipf("headless windows not supported: %v", err)
|
|
||||||
}
|
|
||||||
defer w.Release()
|
|
||||||
|
|
||||||
col := color.RGBA{A: 0xff, R: 0xcc, G: 0xcc}
|
sz := w.size
|
||||||
|
col := color.RGBA{A: 0xff, R: 0xca, G: 0xfe}
|
||||||
var ops op.Ops
|
var ops op.Ops
|
||||||
paint.ColorOp{Color: col}.Add(&ops)
|
paint.ColorOp{Color: col}.Add(&ops)
|
||||||
// Paint only part of the screen to avoid the glClear optimization.
|
// Paint only part of the screen to avoid the glClear optimization.
|
||||||
@@ -41,3 +38,15 @@ func TestHeadless(t *testing.T) {
|
|||||||
t.Errorf("got color %v, expected %v", got, col)
|
t.Errorf("got color %v, expected %v", got, col)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,123 @@
|
|||||||
|
// Code generated by build.go. DO NOT EDIT.
|
||||||
|
|
||||||
|
package headless
|
||||||
|
|
||||||
|
import "gioui.org/gpu/backend"
|
||||||
|
|
||||||
|
var (
|
||||||
|
shader_input_vert = backend.ShaderSources{
|
||||||
|
Inputs: []backend.InputLocation{backend.InputLocation{Name: "position", Location: 0, Semantic: "POSITION", SemanticIndex: 0, Type: 0x0, Size: 4}},
|
||||||
|
GLSL100ES: "#version 100\n\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",
|
||||||
|
/*
|
||||||
|
static float4 gl_Position;
|
||||||
|
static float4 position;
|
||||||
|
|
||||||
|
struct SPIRV_Cross_Input
|
||||||
|
{
|
||||||
|
float4 position : POSITION;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SPIRV_Cross_Output
|
||||||
|
{
|
||||||
|
float4 gl_Position : SV_Position;
|
||||||
|
};
|
||||||
|
|
||||||
|
void vert_main()
|
||||||
|
{
|
||||||
|
gl_Position = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
|
||||||
|
{
|
||||||
|
position = stage_input.position;
|
||||||
|
vert_main();
|
||||||
|
SPIRV_Cross_Output stage_output;
|
||||||
|
stage_output.gl_Position = gl_Position;
|
||||||
|
return stage_output;
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
HLSL: []byte(nil),
|
||||||
|
}
|
||||||
|
shader_simple_frag = backend.ShaderSources{
|
||||||
|
GLSL100ES: "#version 100\nprecision mediump float;\nprecision highp int;\n\nvoid main()\n{\n gl_FragData[0] = vec4(0.25, 0.5, 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.5, 0.75, 1.0);\n}\n\n",
|
||||||
|
/*
|
||||||
|
static float4 fragColor;
|
||||||
|
|
||||||
|
struct SPIRV_Cross_Output
|
||||||
|
{
|
||||||
|
float4 fragColor : SV_Target0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void frag_main()
|
||||||
|
{
|
||||||
|
fragColor = float4(0.25f, 0.5f, 0.75f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPIRV_Cross_Output main()
|
||||||
|
{
|
||||||
|
frag_main();
|
||||||
|
SPIRV_Cross_Output stage_output;
|
||||||
|
stage_output.fragColor = fragColor;
|
||||||
|
return stage_output;
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
HLSL: []byte(nil),
|
||||||
|
}
|
||||||
|
shader_simple_vert = backend.ShaderSources{
|
||||||
|
GLSL100ES: "#version 100\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",
|
||||||
|
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",
|
||||||
|
/*
|
||||||
|
static float4 gl_Position;
|
||||||
|
static int gl_VertexIndex;
|
||||||
|
struct SPIRV_Cross_Input
|
||||||
|
{
|
||||||
|
uint gl_VertexIndex : SV_VertexID;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SPIRV_Cross_Output
|
||||||
|
{
|
||||||
|
float4 gl_Position : SV_Position;
|
||||||
|
};
|
||||||
|
|
||||||
|
void vert_main()
|
||||||
|
{
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
if (gl_VertexIndex == 0)
|
||||||
|
{
|
||||||
|
x = 0.0f;
|
||||||
|
y = 0.5f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (gl_VertexIndex == 1)
|
||||||
|
{
|
||||||
|
x = 0.5f;
|
||||||
|
y = -0.5f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
x = -0.5f;
|
||||||
|
y = -0.5f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gl_Position = float4(x, y, 0.5f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
|
||||||
|
{
|
||||||
|
gl_VertexIndex = int(stage_input.gl_VertexIndex);
|
||||||
|
vert_main();
|
||||||
|
SPIRV_Cross_Output stage_output;
|
||||||
|
stage_output.gl_Position = gl_Position;
|
||||||
|
return stage_output;
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
HLSL: []byte(nil),
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -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