forked from joejulian/gio
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:
+26
-21
@@ -623,17 +623,18 @@ func (g *compute) frame(target RenderTarget) error {
|
||||
return err
|
||||
}
|
||||
t.render.end()
|
||||
var d driver.LoadDesc
|
||||
d := driver.LoadDesc{
|
||||
ClearColor: g.collector.clearColor,
|
||||
}
|
||||
if g.collector.clear {
|
||||
g.collector.clear = false
|
||||
d.Action = driver.LoadActionClear
|
||||
c := &d.ClearColor
|
||||
c.R, c.G, c.B, c.A = g.collector.clearColor.Float32()
|
||||
}
|
||||
g.ctx.BindFramebuffer(defFBO, d)
|
||||
g.ctx.BeginRenderPass(defFBO, d)
|
||||
t.blit.begin()
|
||||
g.blitLayers(viewport)
|
||||
t.blit.end()
|
||||
g.ctx.EndRenderPass()
|
||||
t.compact.begin()
|
||||
if err := g.compactAllocs(); err != nil {
|
||||
return err
|
||||
@@ -993,18 +994,18 @@ func (g *compute) renderMaterials() error {
|
||||
m.vert.uniforms.scale = [2]float32{2, -2}
|
||||
m.vert.uniforms.pos = [2]float32{-1, +1}
|
||||
m.vert.buf.Upload(byteslice.Struct(m.vert.uniforms))
|
||||
g.ctx.BindVertexUniforms(m.vert.buf)
|
||||
g.ctx.BindFragmentUniforms(m.frag.buf)
|
||||
vertexData := byteslice.Slice(m.quads)
|
||||
n := pow2Ceil(len(vertexData))
|
||||
m.buffer.ensureCapacity(false, g.ctx, driver.BufferBindingVertices, n)
|
||||
m.buffer.buffer.Upload(vertexData)
|
||||
g.ctx.BindTexture(0, imgAtlas.image)
|
||||
var d driver.LoadDesc
|
||||
if !realized {
|
||||
d.Action = driver.LoadActionClear
|
||||
}
|
||||
g.ctx.BindFramebuffer(atlas.fbo, d)
|
||||
g.ctx.BeginRenderPass(atlas.fbo, d)
|
||||
g.ctx.BindVertexUniforms(m.vert.buf)
|
||||
g.ctx.BindFragmentUniforms(m.frag.buf)
|
||||
g.ctx.BindTexture(0, imgAtlas.image)
|
||||
g.ctx.BindPipeline(m.pipeline)
|
||||
g.ctx.BindVertexBuffer(m.buffer.buffer, 0)
|
||||
newAllocs := atlas.allocs[allocStart:]
|
||||
@@ -1013,6 +1014,7 @@ func (g *compute) renderMaterials() error {
|
||||
g.ctx.Viewport(a.rect.Min.X, a.rect.Min.Y, sz.X, sz.Y)
|
||||
g.ctx.DrawArrays(driver.DrawModeTriangles, i*6, 6)
|
||||
}
|
||||
g.ctx.EndRenderPass()
|
||||
if !g.useCPU {
|
||||
continue
|
||||
}
|
||||
@@ -1263,18 +1265,6 @@ func (g *compute) render(images *textureAtlas, dst driver.Texture, cpuDst cpu.Im
|
||||
}
|
||||
}
|
||||
|
||||
if !g.useCPU {
|
||||
g.ctx.BindImageTexture(kernel4OutputUnit, dst, driver.AccessWrite, driver.TextureFormatRGBA8)
|
||||
if images != nil {
|
||||
g.ctx.BindImageTexture(kernel4AtlasUnit, images.image, driver.AccessRead, driver.TextureFormatRGBA8)
|
||||
}
|
||||
} else {
|
||||
*g.output.descriptors.Binding2() = cpuDst
|
||||
if images != nil {
|
||||
*g.output.descriptors.Binding3() = images.cpuImage
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
*g.memHeader = memoryHeader{
|
||||
mem_offset: alloc,
|
||||
@@ -1282,6 +1272,19 @@ func (g *compute) render(images *textureAtlas, dst driver.Texture, cpuDst cpu.Im
|
||||
g.buffers.memory.upload(byteslice.Struct(g.memHeader))
|
||||
g.buffers.state.upload(g.zeros(clearSize))
|
||||
|
||||
if !g.useCPU {
|
||||
g.ctx.BeginCompute()
|
||||
g.ctx.BindImageTexture(kernel4OutputUnit, dst, driver.AccessWrite, driver.TextureFormatRGBA8)
|
||||
if images != nil {
|
||||
g.ctx.BindImageTexture(kernel4AtlasUnit, images.image, driver.AccessRead, driver.TextureFormatRGBA8)
|
||||
}
|
||||
} else {
|
||||
*g.output.descriptors.Binding2() = cpuDst
|
||||
if images != nil {
|
||||
*g.output.descriptors.Binding3() = images.cpuImage
|
||||
}
|
||||
}
|
||||
|
||||
g.bindBuffers()
|
||||
g.memoryBarrier()
|
||||
g.dispatch(g.programs.elements, numPartitions, 1, 1)
|
||||
@@ -1298,7 +1301,9 @@ func (g *compute) render(images *textureAtlas, dst driver.Texture, cpuDst cpu.Im
|
||||
g.memoryBarrier()
|
||||
g.dispatch(g.programs.kernel4, tileDims.X, tileDims.Y, 1)
|
||||
g.memoryBarrier()
|
||||
if g.useCPU {
|
||||
if !g.useCPU {
|
||||
g.ctx.EndCompute()
|
||||
} else {
|
||||
g.dispatcher.Sync()
|
||||
}
|
||||
|
||||
|
||||
+41
-11
@@ -424,7 +424,6 @@ func (g *gpu) frame(target RenderTarget) error {
|
||||
for _, img := range g.drawOps.imageOps {
|
||||
expandPathOp(img.path, img.clip)
|
||||
}
|
||||
g.ctx.Viewport(0, 0, viewport.X, viewport.Y)
|
||||
g.stencilTimer.begin()
|
||||
g.renderer.packStencils(&g.drawOps.pathOps)
|
||||
g.renderer.stencilClips(g.drawOps.pathCache, g.drawOps.pathOps)
|
||||
@@ -432,17 +431,19 @@ func (g *gpu) frame(target RenderTarget) error {
|
||||
g.renderer.intersect(g.drawOps.imageOps)
|
||||
g.stencilTimer.end()
|
||||
g.coverTimer.begin()
|
||||
var d driver.LoadDesc
|
||||
g.renderer.uploadImages(g.cache, g.drawOps.imageOps)
|
||||
d := driver.LoadDesc{
|
||||
ClearColor: g.drawOps.clearColor,
|
||||
}
|
||||
if g.drawOps.clear {
|
||||
g.drawOps.clear = false
|
||||
d.Action = driver.LoadActionClear
|
||||
c := &d.ClearColor
|
||||
c.R, c.G, c.B, c.A = g.drawOps.clearColor.Float32()
|
||||
}
|
||||
g.ctx.BindFramebuffer(defFBO, d)
|
||||
g.ctx.BeginRenderPass(defFBO, d)
|
||||
g.ctx.Viewport(0, 0, viewport.X, viewport.Y)
|
||||
g.renderer.drawOps(g.cache, g.drawOps.imageOps)
|
||||
g.coverTimer.end()
|
||||
g.ctx.EndRenderPass()
|
||||
g.cleanupTimer.begin()
|
||||
g.cache.frame()
|
||||
g.drawOps.pathCache.frame()
|
||||
@@ -669,13 +670,21 @@ func (r *renderer) stencilClips(pathCache *opCache, ops []*pathOp) {
|
||||
r.pather.begin(r.packer.sizes)
|
||||
for _, p := range ops {
|
||||
if fbo != p.place.Idx {
|
||||
if fbo != -1 {
|
||||
r.ctx.EndRenderPass()
|
||||
}
|
||||
fbo = p.place.Idx
|
||||
f := r.pather.stenciler.cover(fbo)
|
||||
r.ctx.BindFramebuffer(f.fbo, driver.LoadDesc{Action: driver.LoadActionClear})
|
||||
r.ctx.BeginRenderPass(f.fbo, driver.LoadDesc{Action: driver.LoadActionClear})
|
||||
r.ctx.BindPipeline(r.pather.stenciler.pipeline.pipeline.pipeline)
|
||||
r.ctx.BindIndexBuffer(r.pather.stenciler.indexBuf)
|
||||
}
|
||||
v, _ := pathCache.get(p.pathKey)
|
||||
r.pather.stencilPath(p.clip, p.off, p.place.Pos, v.data)
|
||||
}
|
||||
if fbo != -1 {
|
||||
r.ctx.EndRenderPass()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *renderer) intersect(ops []imageOp) {
|
||||
@@ -684,21 +693,28 @@ func (r *renderer) intersect(ops []imageOp) {
|
||||
}
|
||||
fbo := -1
|
||||
r.pather.stenciler.beginIntersect(r.intersections.sizes)
|
||||
r.ctx.BindVertexBuffer(r.blitter.quadVerts, 0)
|
||||
for _, img := range ops {
|
||||
if img.clipType != clipTypeIntersection {
|
||||
continue
|
||||
}
|
||||
if fbo != img.place.Idx {
|
||||
if fbo != -1 {
|
||||
r.ctx.EndRenderPass()
|
||||
}
|
||||
fbo = img.place.Idx
|
||||
f := r.pather.stenciler.intersections.fbos[fbo]
|
||||
d := driver.LoadDesc{Action: driver.LoadActionClear}
|
||||
d.ClearColor.R = 1.0
|
||||
r.ctx.BindFramebuffer(f.fbo, d)
|
||||
r.ctx.BeginRenderPass(f.fbo, d)
|
||||
r.ctx.BindPipeline(r.pather.stenciler.ipipeline.pipeline.pipeline)
|
||||
r.ctx.BindVertexBuffer(r.blitter.quadVerts, 0)
|
||||
}
|
||||
r.ctx.Viewport(img.place.Pos.X, img.place.Pos.Y, img.clip.Dx(), img.clip.Dy())
|
||||
r.intersectPath(img.path, img.clip)
|
||||
}
|
||||
if fbo != -1 {
|
||||
r.ctx.EndRenderPass()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *renderer) intersectPath(p *pathOp, clip image.Rectangle) {
|
||||
@@ -1077,8 +1093,16 @@ func (d *drawState) materialFor(rect f32.Rectangle, off f32.Point, partTrans f32
|
||||
return m
|
||||
}
|
||||
|
||||
func (r *renderer) uploadImages(cache *resourceCache, ops []imageOp) {
|
||||
for _, img := range ops {
|
||||
m := img.material
|
||||
if m.material == materialTexture {
|
||||
r.texHandle(cache, m.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *renderer) drawOps(cache *resourceCache, ops []imageOp) {
|
||||
r.ctx.BindVertexBuffer(r.blitter.quadVerts, 0)
|
||||
var coverTex driver.Texture
|
||||
for _, img := range ops {
|
||||
m := img.material
|
||||
@@ -1092,6 +1116,9 @@ func (r *renderer) drawOps(cache *resourceCache, ops []imageOp) {
|
||||
var fbo stencilFBO
|
||||
switch img.clipType {
|
||||
case clipTypeNone:
|
||||
p := r.blitter.pipelines[m.material]
|
||||
r.ctx.BindPipeline(p.pipeline)
|
||||
r.ctx.BindVertexBuffer(r.blitter.quadVerts, 0)
|
||||
r.blitter.blit(m.material, m.color, m.color1, m.color2, scale, off, m.uvTrans)
|
||||
continue
|
||||
case clipTypePath:
|
||||
@@ -1108,6 +1135,9 @@ func (r *renderer) drawOps(cache *resourceCache, ops []imageOp) {
|
||||
Max: img.place.Pos.Add(drc.Size()),
|
||||
}
|
||||
coverScale, coverOff := texSpaceTransform(layout.FRect(uv), fbo.size)
|
||||
p := r.pather.coverer.pipelines[m.material]
|
||||
r.ctx.BindPipeline(p.pipeline)
|
||||
r.ctx.BindVertexBuffer(r.blitter.quadVerts, 0)
|
||||
r.pather.cover(m.material, m.color, m.color1, m.color2, scale, off, m.uvTrans, coverScale, coverOff)
|
||||
}
|
||||
}
|
||||
@@ -1165,12 +1195,12 @@ func (u *uniformBuffer) Release() {
|
||||
|
||||
func (p *pipeline) UploadUniforms(ctx driver.Device) {
|
||||
if p.vertUniforms != nil {
|
||||
ctx.BindVertexUniforms(p.vertUniforms.buf)
|
||||
p.vertUniforms.Upload()
|
||||
ctx.BindVertexUniforms(p.vertUniforms.buf)
|
||||
}
|
||||
if p.fragUniforms != nil {
|
||||
ctx.BindFragmentUniforms(p.fragUniforms.buf)
|
||||
p.fragUniforms.Upload()
|
||||
ctx.BindFragmentUniforms(p.fragUniforms.buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+34
-24
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -789,7 +789,13 @@ func (f *Framebuffer) ReadPixels(src image.Rectangle, pixels []byte, stride int)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Backend) BindFramebuffer(fbo driver.Framebuffer, d driver.LoadDesc) {
|
||||
func (b *Backend) BeginCompute() {
|
||||
}
|
||||
|
||||
func (b *Backend) EndCompute() {
|
||||
}
|
||||
|
||||
func (b *Backend) BeginRenderPass(fbo driver.Framebuffer, d driver.LoadDesc) {
|
||||
b.fbo = fbo.(*Framebuffer)
|
||||
b.ctx.OMSetRenderTargets(b.fbo.renderTarget, nil)
|
||||
if d.Action == driver.LoadActionClear {
|
||||
@@ -799,6 +805,9 @@ func (b *Backend) BindFramebuffer(fbo driver.Framebuffer, d driver.LoadDesc) {
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Backend) EndRenderPass() {
|
||||
}
|
||||
|
||||
func (f *Framebuffer) Release() {
|
||||
if f.foreign {
|
||||
panic("framebuffer not created by NewFramebuffer")
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"image"
|
||||
"time"
|
||||
|
||||
"gioui.org/internal/f32color"
|
||||
"gioui.org/shader"
|
||||
)
|
||||
|
||||
@@ -34,9 +35,10 @@ type Device interface {
|
||||
DrawArrays(mode DrawMode, off, count int)
|
||||
DrawElements(mode DrawMode, off, count int)
|
||||
|
||||
BeginRenderPass(f Framebuffer, desc LoadDesc)
|
||||
EndRenderPass()
|
||||
BindProgram(p Program)
|
||||
BindPipeline(p Pipeline)
|
||||
BindFramebuffer(f Framebuffer, a LoadDesc)
|
||||
BindTexture(unit int, t Texture)
|
||||
BindVertexBuffer(b Buffer, offset int)
|
||||
BindIndexBuffer(b Buffer)
|
||||
@@ -45,6 +47,8 @@ type Device interface {
|
||||
BindFragmentUniforms(buf Buffer)
|
||||
BindStorageBuffer(binding int, buf Buffer)
|
||||
|
||||
BeginCompute()
|
||||
EndCompute()
|
||||
CopyTexture(dst Texture, dstOrigin image.Point, src Framebuffer, srcRect image.Rectangle)
|
||||
MemoryBarrier()
|
||||
DispatchCompute(x, y, z int)
|
||||
@@ -54,12 +58,7 @@ type Device interface {
|
||||
|
||||
type LoadDesc struct {
|
||||
Action LoadAction
|
||||
ClearColor struct {
|
||||
R float32
|
||||
G float32
|
||||
B float32
|
||||
A float32
|
||||
}
|
||||
ClearColor f32color.RGBA
|
||||
}
|
||||
|
||||
type Pipeline interface {
|
||||
|
||||
+122
-154
@@ -387,12 +387,12 @@ type Backend struct {
|
||||
computeEnc C.CFTypeRef
|
||||
blitEnc C.CFTypeRef
|
||||
|
||||
prog *Program
|
||||
|
||||
stagingBuf C.CFTypeRef
|
||||
stagingOff int
|
||||
|
||||
indexBuf *Buffer
|
||||
state state
|
||||
newState state
|
||||
|
||||
// bufSizes is scratch space for filling out the spvBufferSizeConstants
|
||||
// that spirv-cross generates for emulating buffer.length expressions in
|
||||
@@ -400,25 +400,6 @@ type Backend struct {
|
||||
bufSizes []uint32
|
||||
}
|
||||
|
||||
type state struct {
|
||||
renderPass struct {
|
||||
framebuffer *Framebuffer
|
||||
loadAction driver.LoadAction
|
||||
clearColor [4]float32
|
||||
}
|
||||
pipeline *Pipeline
|
||||
program *Program
|
||||
vertices struct {
|
||||
buffer *Buffer
|
||||
offset int
|
||||
}
|
||||
buffers [bufferUnits]*Buffer
|
||||
vertUniforms *Buffer
|
||||
fragUniforms *Buffer
|
||||
textures [texUnits]*Texture
|
||||
viewport C.MTLViewport
|
||||
}
|
||||
|
||||
type Texture struct {
|
||||
backend *Backend
|
||||
texture C.CFTypeRef
|
||||
@@ -517,7 +498,6 @@ func (b *Backend) startBlit() C.CFTypeRef {
|
||||
if b.blitEnc == 0 {
|
||||
panic("metal: [MTLCommandBuffer blitCommandEncoder:] failed")
|
||||
}
|
||||
b.state = state{}
|
||||
return b.blitEnc
|
||||
}
|
||||
|
||||
@@ -805,24 +785,34 @@ func (b *Backend) newShader(src shader.Sources) (*Shader, error) {
|
||||
}
|
||||
|
||||
func (b *Backend) Viewport(x, y, width, height int) {
|
||||
b.newState.viewport = C.MTLViewport{
|
||||
enc := b.renderEnc
|
||||
if enc == 0 {
|
||||
panic("no active render pass")
|
||||
}
|
||||
C.renderEncViewport(enc, C.MTLViewport{
|
||||
originX: C.double(x),
|
||||
originY: C.double(y),
|
||||
width: C.double(width),
|
||||
height: C.double(height),
|
||||
znear: 0.0,
|
||||
zfar: 1.0,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (b *Backend) DrawArrays(mode driver.DrawMode, off, count int) {
|
||||
enc := b.encodeState()
|
||||
enc := b.renderEnc
|
||||
if enc == 0 {
|
||||
panic("no active render pass")
|
||||
}
|
||||
t := primitiveFor(mode)
|
||||
C.renderEncDrawPrimitives(enc, t, C.NSUInteger(off), C.NSUInteger(count))
|
||||
}
|
||||
|
||||
func (b *Backend) DrawElements(mode driver.DrawMode, off, count int) {
|
||||
enc := b.encodeState()
|
||||
enc := b.renderEnc
|
||||
if enc == 0 {
|
||||
panic("no active render pass")
|
||||
}
|
||||
t := primitiveFor(mode)
|
||||
C.renderEncDrawIndexedPrimitives(enc, t, b.indexBuf.buffer, C.NSUInteger(off), C.NSUInteger(count))
|
||||
}
|
||||
@@ -839,59 +829,46 @@ func primitiveFor(mode driver.DrawMode) C.MTLPrimitiveType {
|
||||
}
|
||||
|
||||
func (b *Backend) BindImageTexture(unit int, tex driver.Texture, access driver.AccessBits, f driver.TextureFormat) {
|
||||
b.newState.textures[unit] = tex.(*Texture)
|
||||
b.BindTexture(unit, tex)
|
||||
}
|
||||
|
||||
func (b *Backend) MemoryBarrier() {}
|
||||
|
||||
func (b *Backend) startCompute() C.CFTypeRef {
|
||||
if b.computeEnc != 0 {
|
||||
return b.computeEnc
|
||||
}
|
||||
func (b *Backend) BeginCompute() {
|
||||
b.endEncoder()
|
||||
b.ensureCmdBuffer()
|
||||
for i := range b.bufSizes {
|
||||
b.bufSizes[i] = 0
|
||||
}
|
||||
b.computeEnc = C.cmdBufferComputeEncoder(b.cmdBuffer)
|
||||
if b.computeEnc == 0 {
|
||||
panic("metal: [MTLCommandBuffer computeCommandEncoder:] failed")
|
||||
}
|
||||
b.state = state{}
|
||||
return b.computeEnc
|
||||
}
|
||||
|
||||
func (b *Backend) EndCompute() {
|
||||
if b.computeEnc == 0 {
|
||||
panic("no active compute pass")
|
||||
}
|
||||
C.computeEncEnd(b.computeEnc)
|
||||
C.CFRelease(b.computeEnc)
|
||||
b.computeEnc = 0
|
||||
}
|
||||
|
||||
func (b *Backend) DispatchCompute(x, y, z int) {
|
||||
enc := b.startCompute()
|
||||
p := b.newState.program
|
||||
if p != b.state.program {
|
||||
C.computeEncSetPipeline(enc, p.pipeline)
|
||||
}
|
||||
for i, t := range b.newState.textures {
|
||||
current := b.state.textures[i]
|
||||
if t != current {
|
||||
C.computeEncSetTexture(enc, C.NSUInteger(i), t.texture)
|
||||
}
|
||||
}
|
||||
for i, buf := range b.newState.buffers {
|
||||
b.bufSizes[i] = uint32(buf.size)
|
||||
current := b.state.buffers[i]
|
||||
if buf.buffer != 0 {
|
||||
if buf != current {
|
||||
C.computeEncSetBuffer(enc, C.NSUInteger(i), buf.buffer)
|
||||
}
|
||||
} else if buf.size > 0 {
|
||||
C.computeEncSetBytes(enc, unsafe.Pointer(&buf.store[0]), C.NSUInteger(buf.size), C.NSUInteger(i))
|
||||
}
|
||||
}
|
||||
if n := len(b.newState.buffers); n > 0 {
|
||||
C.computeEncSetBytes(enc, unsafe.Pointer(&b.bufSizes[0]), C.NSUInteger(n*4), spvBufferSizeConstantsBinding)
|
||||
enc := b.computeEnc
|
||||
if enc == 0 {
|
||||
panic("no active compute pass")
|
||||
}
|
||||
C.computeEncSetBytes(enc, unsafe.Pointer(&b.bufSizes[0]), C.NSUInteger(len(b.bufSizes)*4), spvBufferSizeConstantsBinding)
|
||||
threadgroupsPerGrid := C.MTLSize{
|
||||
width: C.NSUInteger(x), height: C.NSUInteger(y), depth: C.NSUInteger(z),
|
||||
}
|
||||
sz := b.prog.groupSize
|
||||
threadsPerThreadgroup := C.MTLSize{
|
||||
width: C.NSUInteger(p.groupSize[0]), height: C.NSUInteger(p.groupSize[1]), depth: C.NSUInteger(p.groupSize[2]),
|
||||
width: C.NSUInteger(sz[0]), height: C.NSUInteger(sz[1]), depth: C.NSUInteger(sz[2]),
|
||||
}
|
||||
C.computeEncDispatch(enc, threadgroupsPerGrid, threadsPerThreadgroup)
|
||||
b.state = b.newState
|
||||
}
|
||||
|
||||
func (b *Backend) stagingBuffer(size int) (C.CFTypeRef, int) {
|
||||
@@ -956,34 +933,14 @@ func (p *Pipeline) Release() {
|
||||
|
||||
func (b *Backend) BindTexture(unit int, tex driver.Texture) {
|
||||
t := tex.(*Texture)
|
||||
b.newState.textures[unit] = t
|
||||
}
|
||||
|
||||
func (b *Backend) beginPass() C.CFTypeRef {
|
||||
r := b.newState.renderPass
|
||||
if r == b.state.renderPass {
|
||||
return b.renderEnc
|
||||
if enc := b.renderEnc; enc != 0 {
|
||||
C.renderEncSetFragmentTexture(enc, C.NSUInteger(unit), t.texture)
|
||||
C.renderEncSetFragmentSamplerState(enc, C.NSUInteger(unit), t.sampler)
|
||||
} else if enc := b.computeEnc; enc != 0 {
|
||||
C.computeEncSetTexture(enc, C.NSUInteger(unit), t.texture)
|
||||
} else {
|
||||
panic("no active render nor compute pass")
|
||||
}
|
||||
b.endEncoder()
|
||||
var act C.MTLLoadAction
|
||||
switch r.loadAction {
|
||||
case driver.LoadActionKeep:
|
||||
act = C.MTLLoadActionLoad
|
||||
case driver.LoadActionClear:
|
||||
act = C.MTLLoadActionClear
|
||||
case driver.LoadActionInvalidate:
|
||||
act = C.MTLLoadActionDontCare
|
||||
}
|
||||
b.ensureCmdBuffer()
|
||||
c := r.clearColor
|
||||
b.renderEnc = C.cmdBufferRenderEncoder(b.cmdBuffer, r.framebuffer.texture, act, C.float(c[0]), C.float(c[1]), C.float(c[2]), C.float(c[3]))
|
||||
if b.renderEnc == 0 {
|
||||
panic("metal: [MTLCommandBuffer renderCommandEncoderWithDescriptor:] failed")
|
||||
}
|
||||
r.loadAction = driver.LoadActionKeep
|
||||
b.newState.renderPass = r
|
||||
b.state.renderPass = r
|
||||
return b.renderEnc
|
||||
}
|
||||
|
||||
func (b *Backend) ensureCmdBuffer() {
|
||||
@@ -996,59 +953,23 @@ func (b *Backend) ensureCmdBuffer() {
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Backend) encodeState() C.CFTypeRef {
|
||||
enc := b.beginPass()
|
||||
for i, t := range b.newState.textures {
|
||||
current := b.state.textures[i]
|
||||
if t != current {
|
||||
C.renderEncSetFragmentTexture(enc, C.NSUInteger(i), t.texture)
|
||||
C.renderEncSetFragmentSamplerState(enc, C.NSUInteger(i), t.sampler)
|
||||
}
|
||||
}
|
||||
if p := b.newState.pipeline; p != b.state.pipeline {
|
||||
C.renderEncSetRenderPipelineState(enc, p.pipeline)
|
||||
}
|
||||
if bf := b.newState.fragUniforms; bf != nil {
|
||||
if bf.buffer != 0 {
|
||||
if bf != b.state.fragUniforms {
|
||||
C.renderEncSetFragmentBuffer(enc, bf.buffer, uniformBufferIndex, 0)
|
||||
}
|
||||
} else if bf.size > 0 {
|
||||
C.renderEncSetFragmentBytes(enc, unsafe.Pointer(&bf.store[0]), C.NSUInteger(bf.size), uniformBufferIndex)
|
||||
}
|
||||
}
|
||||
if bf := b.newState.vertUniforms; bf != nil {
|
||||
if bf.buffer != 0 {
|
||||
if bf != b.state.vertUniforms {
|
||||
C.renderEncSetVertexBuffer(enc, bf.buffer, uniformBufferIndex, 0)
|
||||
}
|
||||
} else if bf.size > 0 {
|
||||
C.renderEncSetVertexBytes(enc, unsafe.Pointer(&bf.store[0]), C.NSUInteger(bf.size), uniformBufferIndex)
|
||||
}
|
||||
}
|
||||
if v := b.newState.vertices; v.buffer != nil {
|
||||
if v.buffer.buffer != 0 {
|
||||
if v != b.state.vertices {
|
||||
C.renderEncSetVertexBuffer(enc, v.buffer.buffer, attributeBufferIndex, C.NSUInteger(v.offset))
|
||||
}
|
||||
} else if n := v.buffer.size - v.offset; n > 0 {
|
||||
C.renderEncSetVertexBytes(enc, unsafe.Pointer(&v.buffer.store[v.offset]), C.NSUInteger(n), attributeBufferIndex)
|
||||
}
|
||||
}
|
||||
if vp := b.newState.viewport; vp != b.state.viewport {
|
||||
C.renderEncViewport(enc, vp)
|
||||
}
|
||||
b.state = b.newState
|
||||
return enc
|
||||
}
|
||||
|
||||
func (b *Backend) BindPipeline(pipe driver.Pipeline) {
|
||||
p := pipe.(*Pipeline)
|
||||
b.newState.pipeline = p
|
||||
enc := b.renderEnc
|
||||
if enc == 0 {
|
||||
panic("no active render pass")
|
||||
}
|
||||
C.renderEncSetRenderPipelineState(enc, p.pipeline)
|
||||
}
|
||||
|
||||
func (b *Backend) BindProgram(prog driver.Program) {
|
||||
b.newState.program = prog.(*Program)
|
||||
enc := b.computeEnc
|
||||
if enc == 0 {
|
||||
panic("no active compute pass")
|
||||
}
|
||||
p := prog.(*Program)
|
||||
C.computeEncSetPipeline(enc, p.pipeline)
|
||||
b.prog = p
|
||||
}
|
||||
|
||||
func (s *Shader) Release() {
|
||||
@@ -1062,23 +983,56 @@ func (p *Program) Release() {
|
||||
}
|
||||
|
||||
func (b *Backend) BindStorageBuffer(binding int, buffer driver.Buffer) {
|
||||
b.newState.buffers[binding] = buffer.(*Buffer)
|
||||
buf := buffer.(*Buffer)
|
||||
b.bufSizes[binding] = uint32(buf.size)
|
||||
enc := b.computeEnc
|
||||
if enc == 0 {
|
||||
panic("no active compute pass")
|
||||
}
|
||||
if buf.buffer != 0 {
|
||||
C.computeEncSetBuffer(enc, C.NSUInteger(binding), buf.buffer)
|
||||
} else if buf.size > 0 {
|
||||
C.computeEncSetBytes(enc, unsafe.Pointer(&buf.store[0]), C.NSUInteger(buf.size), C.NSUInteger(binding))
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Backend) BindVertexUniforms(buf driver.Buffer) {
|
||||
bf := buf.(*Buffer)
|
||||
b.newState.vertUniforms = bf
|
||||
enc := b.renderEnc
|
||||
if enc == 0 {
|
||||
panic("no active render pass")
|
||||
}
|
||||
if bf.buffer != 0 {
|
||||
C.renderEncSetVertexBuffer(enc, bf.buffer, uniformBufferIndex, 0)
|
||||
} else if bf.size > 0 {
|
||||
C.renderEncSetVertexBytes(enc, unsafe.Pointer(&bf.store[0]), C.NSUInteger(bf.size), uniformBufferIndex)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Backend) BindFragmentUniforms(buf driver.Buffer) {
|
||||
bf := buf.(*Buffer)
|
||||
b.newState.fragUniforms = bf
|
||||
enc := b.renderEnc
|
||||
if enc == 0 {
|
||||
panic("no active render pass")
|
||||
}
|
||||
if bf.buffer != 0 {
|
||||
C.renderEncSetFragmentBuffer(enc, bf.buffer, uniformBufferIndex, 0)
|
||||
} else if bf.size > 0 {
|
||||
C.renderEncSetFragmentBytes(enc, unsafe.Pointer(&bf.store[0]), C.NSUInteger(bf.size), uniformBufferIndex)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Backend) BindVertexBuffer(buf driver.Buffer, offset int) {
|
||||
bf := buf.(*Buffer)
|
||||
b.newState.vertices.buffer = bf
|
||||
b.newState.vertices.offset = offset
|
||||
enc := b.renderEnc
|
||||
if enc == 0 {
|
||||
panic("no active render pass")
|
||||
}
|
||||
if bf.buffer != 0 {
|
||||
C.renderEncSetVertexBuffer(enc, bf.buffer, attributeBufferIndex, C.NSUInteger(offset))
|
||||
} else if n := bf.size - offset; n > 0 {
|
||||
C.renderEncSetVertexBytes(enc, unsafe.Pointer(&bf.store[offset]), C.NSUInteger(n), attributeBufferIndex)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Backend) BindIndexBuffer(buf driver.Buffer) {
|
||||
@@ -1162,32 +1116,46 @@ func (f *Framebuffer) ReadPixels(src image.Rectangle, pixels []byte, stride int)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Backend) BindFramebuffer(fbo driver.Framebuffer, desc driver.LoadDesc) {
|
||||
b.newState.renderPass.framebuffer = fbo.(*Framebuffer)
|
||||
b.newState.renderPass.loadAction = desc.Action
|
||||
c := desc.ClearColor
|
||||
b.newState.renderPass.clearColor = [4]float32{c.R, c.G, c.B, c.A}
|
||||
b.beginPass()
|
||||
func (b *Backend) BeginRenderPass(fbo driver.Framebuffer, d driver.LoadDesc) {
|
||||
b.endEncoder()
|
||||
b.ensureCmdBuffer()
|
||||
f := fbo.(*Framebuffer)
|
||||
col := d.ClearColor
|
||||
var act C.MTLLoadAction
|
||||
switch d.Action {
|
||||
case driver.LoadActionKeep:
|
||||
act = C.MTLLoadActionLoad
|
||||
case driver.LoadActionClear:
|
||||
act = C.MTLLoadActionClear
|
||||
case driver.LoadActionInvalidate:
|
||||
act = C.MTLLoadActionDontCare
|
||||
}
|
||||
b.renderEnc = C.cmdBufferRenderEncoder(b.cmdBuffer, f.texture, act, C.float(col.R), C.float(col.G), C.float(col.B), C.float(col.A))
|
||||
if b.renderEnc == 0 {
|
||||
panic("metal: [MTLCommandBuffer renderCommandEncoderWithDescriptor:] failed")
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Backend) EndRenderPass() {
|
||||
if b.renderEnc == 0 {
|
||||
panic("no active render pass")
|
||||
}
|
||||
C.renderEncEnd(b.renderEnc)
|
||||
C.CFRelease(b.renderEnc)
|
||||
b.renderEnc = 0
|
||||
}
|
||||
|
||||
func (b *Backend) endEncoder() {
|
||||
if b.renderEnc != 0 {
|
||||
C.renderEncEnd(b.renderEnc)
|
||||
C.CFRelease(b.renderEnc)
|
||||
b.renderEnc = 0
|
||||
b.state = state{}
|
||||
panic("active render pass")
|
||||
}
|
||||
if b.computeEnc != 0 {
|
||||
C.computeEncEnd(b.computeEnc)
|
||||
C.CFRelease(b.computeEnc)
|
||||
b.computeEnc = 0
|
||||
b.state = state{}
|
||||
panic("active compute pass")
|
||||
}
|
||||
if b.blitEnc != 0 {
|
||||
C.blitEncEnd(b.blitEnc)
|
||||
C.CFRelease(b.blitEnc)
|
||||
b.blitEnc = 0
|
||||
b.state = state{}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -660,7 +660,7 @@ func (b *Backend) NewFramebuffer(tex driver.Texture) (driver.Framebuffer, error)
|
||||
gltex := tex.(*texture)
|
||||
fb := b.funcs.CreateFramebuffer()
|
||||
fbo := &framebuffer{backend: b, obj: fb}
|
||||
b.BindFramebuffer(fbo, driver.LoadDesc{})
|
||||
b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, fbo.obj)
|
||||
if err := glErr(b.funcs); err != nil {
|
||||
fbo.Release()
|
||||
return nil, err
|
||||
@@ -1194,7 +1194,7 @@ func (b *Backend) CopyTexture(dst driver.Texture, dstOrigin image.Point, src dri
|
||||
|
||||
func (f *framebuffer) ReadPixels(src image.Rectangle, pixels []byte, stride int) error {
|
||||
glErr(f.backend.funcs)
|
||||
f.backend.BindFramebuffer(f, driver.LoadDesc{})
|
||||
f.backend.glstate.bindFramebuffer(f.backend.funcs, gl.FRAMEBUFFER, f.obj)
|
||||
if len(pixels) < src.Dx()*src.Dy()*4 {
|
||||
return errors.New("unexpected RGBA size")
|
||||
}
|
||||
@@ -1217,7 +1217,13 @@ func (b *Backend) BindPipeline(pl driver.Pipeline) {
|
||||
b.BlendFunc(p.blend.SrcFactor, p.blend.DstFactor)
|
||||
}
|
||||
|
||||
func (b *Backend) BindFramebuffer(fbo driver.Framebuffer, desc driver.LoadDesc) {
|
||||
func (b *Backend) BeginCompute() {
|
||||
}
|
||||
|
||||
func (b *Backend) EndCompute() {
|
||||
}
|
||||
|
||||
func (b *Backend) BeginRenderPass(fbo driver.Framebuffer, desc driver.LoadDesc) {
|
||||
b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, fbo.(*framebuffer).obj)
|
||||
switch desc.Action {
|
||||
case driver.LoadActionClear:
|
||||
@@ -1228,6 +1234,9 @@ func (b *Backend) BindFramebuffer(fbo driver.Framebuffer, desc driver.LoadDesc)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Backend) EndRenderPass() {
|
||||
}
|
||||
|
||||
func (f *framebuffer) Release() {
|
||||
if f.foreign {
|
||||
panic("framebuffer not created by NewFramebuffer")
|
||||
|
||||
+1
-6
@@ -346,7 +346,6 @@ func (s *stenciler) beginIntersect(sizes []image.Point) {
|
||||
// floating point formats. Replace with GL_RGB+GL_UNSIGNED_BYTE if
|
||||
// no floating point support is available.
|
||||
s.intersections.resize(s.ctx, sizes)
|
||||
s.ctx.BindPipeline(s.ipipeline.pipeline.pipeline)
|
||||
}
|
||||
|
||||
func (s *stenciler) cover(idx int) stencilFBO {
|
||||
@@ -355,8 +354,6 @@ func (s *stenciler) cover(idx int) stencilFBO {
|
||||
|
||||
func (s *stenciler) begin(sizes []image.Point) {
|
||||
s.fbos.resize(s.ctx, sizes)
|
||||
s.ctx.BindPipeline(s.pipeline.pipeline.pipeline)
|
||||
s.ctx.BindIndexBuffer(s.indexBuf)
|
||||
}
|
||||
|
||||
func (s *stenciler) stencilPath(bounds image.Rectangle, offset f32.Point, uv image.Point, data pathData) {
|
||||
@@ -388,8 +385,6 @@ func (p *pather) cover(mat materialType, col f32color.RGBA, col1, col2 f32color.
|
||||
}
|
||||
|
||||
func (c *coverer) cover(mat materialType, col f32color.RGBA, col1, col2 f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D, coverScale, coverOff f32.Point) {
|
||||
p := c.pipelines[mat]
|
||||
c.ctx.BindPipeline(p.pipeline)
|
||||
var uniforms *coverUniforms
|
||||
switch mat {
|
||||
case materialColor:
|
||||
@@ -411,7 +406,7 @@ func (c *coverer) cover(mat materialType, col f32color.RGBA, col1, col2 f32color
|
||||
}
|
||||
uniforms.transform = [4]float32{scale.X, scale.Y, off.X, off.Y}
|
||||
uniforms.uvCoverTransform = [4]float32{coverScale.X, coverScale.Y, coverOff.X, coverOff.Y}
|
||||
p.UploadUniforms(c.ctx)
|
||||
c.pipelines[mat].UploadUniforms(c.ctx)
|
||||
c.ctx.DrawArrays(driver.DrawModeTriangleStrip, 0, 4)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user