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
+26 -21
View File
@@ -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
View File
@@ -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
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,
+10 -1
View File
@@ -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")
+6 -7
View File
@@ -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
View File
@@ -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{}
}
}
+12 -3
View File
@@ -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
View File
@@ -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)
}