gpu: introduce render passes

Modern GPU API such as Metal and Vulkan use explicit render passes
and command buffers for recording rendering commands. They don't have
global state; each render pass starts with a clean set of bound
textures, pipeline etc.

Change our GPU abstraction to better match newer API and modify our two
renderers to explicitly describe their render passes.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2021-09-03 10:05:35 +02:00
parent af6770de18
commit cc2edbaa51
9 changed files with 252 additions and 228 deletions
+34 -24
View File
@@ -27,7 +27,15 @@ var clearColExpect = f32color.NRGBAToRGBA(clearCol)
func TestFramebufferClear(t *testing.T) {
b := newDriver(t)
sz := image.Point{X: 800, Y: 600}
fbo := setupFBO(t, b, sz)
fbo := newFBO(t, b, sz)
d := driver.LoadDesc{
// ClearColor accepts linear RGBA colors, while 8-bit colors
// are in the sRGB color space.
ClearColor: f32color.LinearFromSRGB(clearCol),
Action: driver.LoadActionClear,
}
b.BeginRenderPass(fbo, d)
b.EndRenderPass()
img := screenshot(t, b, fbo, sz)
if got := img.RGBAAt(0, 0); got != clearColExpect {
t.Errorf("got color %v, expected %v", got, clearColExpect)
@@ -37,13 +45,13 @@ func TestFramebufferClear(t *testing.T) {
func TestSimpleShader(t *testing.T) {
b := newDriver(t)
sz := image.Point{X: 800, Y: 600}
fbo := setupFBO(t, b, sz)
vsh, fsh, err := newShaders(b, gio.Shader_simple_vert, gio.Shader_simple_frag)
if err != nil {
t.Fatal(err)
}
defer vsh.Release()
defer fsh.Release()
fbo := newFBO(t, b, sz)
p, err := b.NewPipeline(driver.PipelineDesc{
VertexShader: vsh,
FragmentShader: fsh,
@@ -53,8 +61,15 @@ func TestSimpleShader(t *testing.T) {
t.Fatal(err)
}
defer p.Release()
d := driver.LoadDesc{
ClearColor: f32color.LinearFromSRGB(clearCol),
Action: driver.LoadActionClear,
}
b.BeginRenderPass(fbo, d)
b.Viewport(0, 0, sz.X, sz.Y)
b.BindPipeline(p)
b.DrawArrays(driver.DrawModeTriangles, 0, 3)
b.EndRenderPass()
img := screenshot(t, b, fbo, sz)
if got := img.RGBAAt(0, 0); got != clearColExpect {
t.Errorf("got color %v, expected %v", got, clearColExpect)
@@ -70,7 +85,6 @@ func TestSimpleShader(t *testing.T) {
func TestInputShader(t *testing.T) {
b := newDriver(t)
sz := image.Point{X: 800, Y: 600}
fbo := setupFBO(t, b, sz)
vsh, fsh, err := newShaders(b, gio.Shader_input_vert, gio.Shader_simple_frag)
if err != nil {
t.Fatal(err)
@@ -87,6 +101,7 @@ func TestInputShader(t *testing.T) {
},
Stride: 4 * 4,
}
fbo := newFBO(t, b, sz)
pipe, err := b.NewPipeline(driver.PipelineDesc{
VertexShader: vsh,
FragmentShader: fsh,
@@ -97,7 +112,6 @@ func TestInputShader(t *testing.T) {
t.Fatal(err)
}
defer pipe.Release()
b.BindPipeline(pipe)
buf, err := b.NewImmutableBuffer(driver.BufferBindingVertices,
byteslice.Slice([]float32{
0, .5, .5, 1,
@@ -109,8 +123,16 @@ func TestInputShader(t *testing.T) {
t.Fatal(err)
}
defer buf.Release()
d := driver.LoadDesc{
ClearColor: f32color.LinearFromSRGB(clearCol),
Action: driver.LoadActionClear,
}
b.BeginRenderPass(fbo, d)
b.Viewport(0, 0, sz.X, sz.Y)
b.BindPipeline(pipe)
b.BindVertexBuffer(buf, 0)
b.DrawArrays(driver.DrawModeTriangles, 0, 3)
b.EndRenderPass()
img := screenshot(t, b, fbo, sz)
if got := img.RGBAAt(0, 0); got != clearColExpect {
t.Errorf("got color %v, expected %v", got, clearColExpect)
@@ -137,19 +159,20 @@ func newShaders(ctx driver.Device, vsrc, fsrc shader.Sources) (vert driver.Verte
func TestFramebuffers(t *testing.T) {
b := newDriver(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}
)
fbo1 := newFBO(t, b, sz)
fbo2 := newFBO(t, b, sz)
fcol1, fcol2 := f32color.LinearFromSRGB(col1), f32color.LinearFromSRGB(col2)
d := driver.LoadDesc{Action: driver.LoadActionClear}
c := &d.ClearColor
c.R, c.G, c.B, c.A = fcol1.Float32()
b.BindFramebuffer(fbo1, d)
c.R, c.G, c.B, c.A = fcol2.Float32()
b.BindFramebuffer(fbo2, d)
d.ClearColor = fcol1
b.BeginRenderPass(fbo1, d)
b.EndRenderPass()
d.ClearColor = fcol2
b.BeginRenderPass(fbo2, d)
b.EndRenderPass()
img := screenshot(t, b, fbo1, sz)
if got := img.RGBAAt(0, 0); got != f32color.NRGBAToRGBA(col1) {
t.Errorf("got color %v, expected %v", got, f32color.NRGBAToRGBA(col1))
@@ -160,19 +183,6 @@ func TestFramebuffers(t *testing.T) {
}
}
func setupFBO(t *testing.T, b driver.Device, size image.Point) driver.Framebuffer {
fbo := newFBO(t, b, size)
// ClearColor accepts linear RGBA colors, while 8-bit colors
// are in the sRGB color space.
col := f32color.LinearFromSRGB(clearCol)
d := driver.LoadDesc{Action: driver.LoadActionClear}
c := &d.ClearColor
c.R, c.G, c.B, c.A = col.Float32()
b.BindFramebuffer(fbo, d)
b.Viewport(0, 0, size.X, size.Y)
return fbo
}
func newFBO(t *testing.T, b driver.Device, size image.Point) driver.Framebuffer {
fboTex, err := b.NewTexture(
driver.TextureFormatSRGBA,
-1
View File
@@ -47,7 +47,6 @@ func NewWindow(width, height int) (*Window, error) {
if err != nil {
return err
}
dev.Viewport(0, 0, width, height)
fboTex, err := dev.NewTexture(
driver.TextureFormatSRGBA,
width, height,