diff --git a/gpu/compute.go b/gpu/compute.go index fcf1910d..01cd373c 100644 --- a/gpu/compute.go +++ b/gpu/compute.go @@ -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() } diff --git a/gpu/gpu.go b/gpu/gpu.go index 3c6849b3..d83a995c 100644 --- a/gpu/gpu.go +++ b/gpu/gpu.go @@ -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) } } diff --git a/gpu/headless/driver_test.go b/gpu/headless/driver_test.go index eb403f77..550edefb 100644 --- a/gpu/headless/driver_test.go +++ b/gpu/headless/driver_test.go @@ -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, diff --git a/gpu/headless/headless.go b/gpu/headless/headless.go index d93c4d20..9d4d95a3 100644 --- a/gpu/headless/headless.go +++ b/gpu/headless/headless.go @@ -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, diff --git a/gpu/internal/d3d11/d3d11_windows.go b/gpu/internal/d3d11/d3d11_windows.go index 57c989f2..3d845c15 100644 --- a/gpu/internal/d3d11/d3d11_windows.go +++ b/gpu/internal/d3d11/d3d11_windows.go @@ -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") diff --git a/gpu/internal/driver/driver.go b/gpu/internal/driver/driver.go index a89cf9e2..9a74096e 100644 --- a/gpu/internal/driver/driver.go +++ b/gpu/internal/driver/driver.go @@ -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 { diff --git a/gpu/internal/metal/metal_darwin.go b/gpu/internal/metal/metal_darwin.go index ba0ded5f..e9b00445 100644 --- a/gpu/internal/metal/metal_darwin.go +++ b/gpu/internal/metal/metal_darwin.go @@ -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{} } } diff --git a/gpu/internal/opengl/opengl.go b/gpu/internal/opengl/opengl.go index 73227b8d..131dd7aa 100644 --- a/gpu/internal/opengl/opengl.go +++ b/gpu/internal/opengl/opengl.go @@ -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") diff --git a/gpu/path.go b/gpu/path.go index 53f87e22..940c4e86 100644 --- a/gpu/path.go +++ b/gpu/path.go @@ -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) }