mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-04 08:55:35 +00:00
gpu: introduce pipeline abstraction
Modern API such as Metal and Vulkan want clients to compile expensive state changes into pipeline objects. Change our GPU driver abstraction to match, thereby paving the way for future drivers. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+87
-65
@@ -281,8 +281,7 @@ type texture struct {
|
||||
type blitter struct {
|
||||
ctx driver.Device
|
||||
viewport image.Point
|
||||
prog [3]*program
|
||||
layout driver.InputLayout
|
||||
pipelines [3]*pipeline
|
||||
colUniforms *blitColUniforms
|
||||
texUniforms *blitTexUniforms
|
||||
linearGradientUniforms *blitLinearGradientUniforms
|
||||
@@ -321,8 +320,8 @@ type uniformBuffer struct {
|
||||
ptr []byte
|
||||
}
|
||||
|
||||
type program struct {
|
||||
prog driver.Program
|
||||
type pipeline struct {
|
||||
pipeline driver.Pipeline
|
||||
vertUniforms *uniformBuffer
|
||||
fragUniforms *uniformBuffer
|
||||
}
|
||||
@@ -453,7 +452,6 @@ func (g *gpu) Frame(target RenderTarget) error {
|
||||
}
|
||||
g.ctx.Viewport(0, 0, viewport.X, viewport.Y)
|
||||
g.stencilTimer.begin()
|
||||
g.ctx.SetBlend(true)
|
||||
g.renderer.packStencils(&g.drawOps.pathOps)
|
||||
g.renderer.stencilClips(g.drawOps.pathCache, g.drawOps.pathOps)
|
||||
g.renderer.packIntersections(g.drawOps.imageOps)
|
||||
@@ -463,7 +461,6 @@ func (g *gpu) Frame(target RenderTarget) error {
|
||||
g.ctx.BindFramebuffer(defFBO)
|
||||
g.ctx.Viewport(0, 0, viewport.X, viewport.Y)
|
||||
g.renderer.drawOps(g.cache, g.drawOps.imageOps)
|
||||
g.ctx.SetBlend(false)
|
||||
g.renderer.pather.stenciler.invalidateFBO()
|
||||
g.coverTimer.end()
|
||||
g.ctx.BindFramebuffer(defFBO)
|
||||
@@ -558,90 +555,128 @@ func newBlitter(ctx driver.Device) *blitter {
|
||||
b.colUniforms = new(blitColUniforms)
|
||||
b.texUniforms = new(blitTexUniforms)
|
||||
b.linearGradientUniforms = new(blitLinearGradientUniforms)
|
||||
prog, layout, err := createColorPrograms(ctx, gio.Shader_blit_vert, gio.Shader_blit_frag,
|
||||
pipelines, err := createColorPrograms(ctx, gio.Shader_blit_vert, gio.Shader_blit_frag,
|
||||
[3]interface{}{&b.colUniforms.vert, &b.linearGradientUniforms.vert, &b.texUniforms.vert},
|
||||
[3]interface{}{&b.colUniforms.frag, &b.linearGradientUniforms.frag, nil},
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
b.prog = prog
|
||||
b.layout = layout
|
||||
b.pipelines = pipelines
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *blitter) release() {
|
||||
b.quadVerts.Release()
|
||||
for _, p := range b.prog {
|
||||
for _, p := range b.pipelines {
|
||||
p.Release()
|
||||
}
|
||||
b.layout.Release()
|
||||
}
|
||||
|
||||
func createColorPrograms(b driver.Device, vsSrc shader.Sources, fsSrc [3]shader.Sources, vertUniforms, fragUniforms [3]interface{}) ([3]*program, driver.InputLayout, error) {
|
||||
var progs [3]*program
|
||||
func createColorPrograms(b driver.Device, vsSrc shader.Sources, fsSrc [3]shader.Sources, vertUniforms, fragUniforms [3]interface{}) ([3]*pipeline, error) {
|
||||
var pipelines [3]*pipeline
|
||||
blend := driver.BlendDesc{
|
||||
Enable: true,
|
||||
SrcFactor: driver.BlendFactorOne,
|
||||
DstFactor: driver.BlendFactorOneMinusSrcAlpha,
|
||||
}
|
||||
layout := []shader.InputDesc{
|
||||
{Type: shader.DataTypeFloat, Size: 2, Offset: 0},
|
||||
{Type: shader.DataTypeFloat, Size: 2, Offset: 4 * 2},
|
||||
}
|
||||
vsh, err := b.NewVertexShader(vsSrc)
|
||||
if err != nil {
|
||||
return pipelines, err
|
||||
}
|
||||
defer vsh.Release()
|
||||
{
|
||||
prog, err := b.NewProgram(vsSrc, fsSrc[materialTexture])
|
||||
fsh, err := b.NewFragmentShader(fsSrc[materialTexture])
|
||||
if err != nil {
|
||||
return progs, nil, err
|
||||
return pipelines, err
|
||||
}
|
||||
defer fsh.Release()
|
||||
pipe, err := b.NewPipeline(driver.PipelineDesc{
|
||||
VertexShader: vsh,
|
||||
FragmentShader: fsh,
|
||||
BlendDesc: blend,
|
||||
VertexLayout: layout,
|
||||
PixelFormat: driver.TextureFormatOutput,
|
||||
})
|
||||
if err != nil {
|
||||
return pipelines, err
|
||||
}
|
||||
var vertBuffer, fragBuffer *uniformBuffer
|
||||
if u := vertUniforms[materialTexture]; u != nil {
|
||||
vertBuffer = newUniformBuffer(b, u)
|
||||
prog.SetVertexUniforms(vertBuffer.buf)
|
||||
}
|
||||
if u := fragUniforms[materialTexture]; u != nil {
|
||||
fragBuffer = newUniformBuffer(b, u)
|
||||
prog.SetFragmentUniforms(fragBuffer.buf)
|
||||
}
|
||||
progs[materialTexture] = newProgram(prog, vertBuffer, fragBuffer)
|
||||
pipelines[materialTexture] = &pipeline{pipe, vertBuffer, fragBuffer}
|
||||
}
|
||||
{
|
||||
var vertBuffer, fragBuffer *uniformBuffer
|
||||
prog, err := b.NewProgram(vsSrc, fsSrc[materialColor])
|
||||
fsh, err := b.NewFragmentShader(fsSrc[materialColor])
|
||||
if err != nil {
|
||||
progs[materialTexture].Release()
|
||||
return progs, nil, err
|
||||
pipelines[materialTexture].Release()
|
||||
return pipelines, err
|
||||
}
|
||||
defer fsh.Release()
|
||||
pipe, err := b.NewPipeline(driver.PipelineDesc{
|
||||
VertexShader: vsh,
|
||||
FragmentShader: fsh,
|
||||
BlendDesc: blend,
|
||||
VertexLayout: layout,
|
||||
PixelFormat: driver.TextureFormatOutput,
|
||||
})
|
||||
if err != nil {
|
||||
pipelines[materialTexture].Release()
|
||||
return pipelines, err
|
||||
}
|
||||
if u := vertUniforms[materialColor]; u != nil {
|
||||
vertBuffer = newUniformBuffer(b, u)
|
||||
prog.SetVertexUniforms(vertBuffer.buf)
|
||||
}
|
||||
if u := fragUniforms[materialColor]; u != nil {
|
||||
fragBuffer = newUniformBuffer(b, u)
|
||||
prog.SetFragmentUniforms(fragBuffer.buf)
|
||||
}
|
||||
progs[materialColor] = newProgram(prog, vertBuffer, fragBuffer)
|
||||
pipelines[materialColor] = &pipeline{pipe, vertBuffer, fragBuffer}
|
||||
}
|
||||
{
|
||||
var vertBuffer, fragBuffer *uniformBuffer
|
||||
prog, err := b.NewProgram(vsSrc, fsSrc[materialLinearGradient])
|
||||
fsh, err := b.NewFragmentShader(fsSrc[materialLinearGradient])
|
||||
if err != nil {
|
||||
progs[materialTexture].Release()
|
||||
progs[materialColor].Release()
|
||||
return progs, nil, err
|
||||
pipelines[materialTexture].Release()
|
||||
pipelines[materialColor].Release()
|
||||
return pipelines, err
|
||||
}
|
||||
defer fsh.Release()
|
||||
pipe, err := b.NewPipeline(driver.PipelineDesc{
|
||||
VertexShader: vsh,
|
||||
FragmentShader: fsh,
|
||||
BlendDesc: blend,
|
||||
VertexLayout: layout,
|
||||
PixelFormat: driver.TextureFormatOutput,
|
||||
})
|
||||
if err != nil {
|
||||
pipelines[materialTexture].Release()
|
||||
pipelines[materialColor].Release()
|
||||
return pipelines, err
|
||||
}
|
||||
if u := vertUniforms[materialLinearGradient]; u != nil {
|
||||
vertBuffer = newUniformBuffer(b, u)
|
||||
prog.SetVertexUniforms(vertBuffer.buf)
|
||||
}
|
||||
if u := fragUniforms[materialLinearGradient]; u != nil {
|
||||
fragBuffer = newUniformBuffer(b, u)
|
||||
prog.SetFragmentUniforms(fragBuffer.buf)
|
||||
}
|
||||
progs[materialLinearGradient] = newProgram(prog, vertBuffer, fragBuffer)
|
||||
pipelines[materialLinearGradient] = &pipeline{pipe, vertBuffer, fragBuffer}
|
||||
}
|
||||
layout, err := b.NewInputLayout(vsSrc, []shader.InputDesc{
|
||||
{Type: shader.DataTypeFloat, Size: 2, Offset: 0},
|
||||
{Type: shader.DataTypeFloat, Size: 2, Offset: 4 * 2},
|
||||
})
|
||||
if err != nil {
|
||||
progs[materialTexture].Release()
|
||||
progs[materialColor].Release()
|
||||
progs[materialLinearGradient].Release()
|
||||
return progs, nil, err
|
||||
for _, p := range pipelines {
|
||||
p.Release()
|
||||
}
|
||||
return pipelines, err
|
||||
}
|
||||
return progs, layout, nil
|
||||
return pipelines, nil
|
||||
}
|
||||
|
||||
func (r *renderer) stencilClips(pathCache *opCache, ops []*pathOp) {
|
||||
@@ -669,7 +704,6 @@ func (r *renderer) intersect(ops []imageOp) {
|
||||
fbo := -1
|
||||
r.pather.stenciler.beginIntersect(r.intersections.sizes)
|
||||
r.ctx.BindVertexBuffer(r.blitter.quadVerts, 4*4, 0)
|
||||
r.ctx.BindInputLayout(r.pather.stenciler.iprog.layout)
|
||||
for _, img := range ops {
|
||||
if img.clipType != clipTypeIntersection {
|
||||
continue
|
||||
@@ -705,9 +739,9 @@ func (r *renderer) intersectPath(p *pathOp, clip image.Rectangle) {
|
||||
r.ctx.BindTexture(0, fbo.tex)
|
||||
coverScale, coverOff := texSpaceTransform(layout.FRect(uv), fbo.size)
|
||||
subScale, subOff := texSpaceTransform(layout.FRect(sub), p.clip.Size())
|
||||
r.pather.stenciler.iprog.uniforms.vert.uvTransform = [4]float32{coverScale.X, coverScale.Y, coverOff.X, coverOff.Y}
|
||||
r.pather.stenciler.iprog.uniforms.vert.subUVTransform = [4]float32{subScale.X, subScale.Y, subOff.X, subOff.Y}
|
||||
r.pather.stenciler.iprog.prog.UploadUniforms()
|
||||
r.pather.stenciler.ipipeline.uniforms.vert.uvTransform = [4]float32{coverScale.X, coverScale.Y, coverOff.X, coverOff.Y}
|
||||
r.pather.stenciler.ipipeline.uniforms.vert.subUVTransform = [4]float32{subScale.X, subScale.Y, subOff.X, subOff.Y}
|
||||
r.pather.stenciler.ipipeline.pipeline.UploadUniforms(r.ctx)
|
||||
r.ctx.DrawArrays(driver.DrawModeTriangleStrip, 0, 4)
|
||||
}
|
||||
|
||||
@@ -1059,9 +1093,7 @@ func (d *drawState) materialFor(rect f32.Rectangle, off f32.Point, partTrans f32
|
||||
}
|
||||
|
||||
func (r *renderer) drawOps(cache *resourceCache, ops []imageOp) {
|
||||
r.ctx.BlendFunc(driver.BlendFactorOne, driver.BlendFactorOneMinusSrcAlpha)
|
||||
r.ctx.BindVertexBuffer(r.blitter.quadVerts, 4*4, 0)
|
||||
r.ctx.BindInputLayout(r.pather.coverer.layout)
|
||||
var coverTex driver.Texture
|
||||
for _, img := range ops {
|
||||
m := img.material
|
||||
@@ -1096,8 +1128,8 @@ func (r *renderer) drawOps(cache *resourceCache, ops []imageOp) {
|
||||
}
|
||||
|
||||
func (b *blitter) blit(mat materialType, col f32color.RGBA, col1, col2 f32color.RGBA, scale, off f32.Point, uvTrans f32.Affine2D) {
|
||||
p := b.prog[mat]
|
||||
b.ctx.BindProgram(p.prog)
|
||||
p := b.pipelines[mat]
|
||||
b.ctx.BindPipeline(p.pipeline)
|
||||
var uniforms *blitUniforms
|
||||
switch mat {
|
||||
case materialColor:
|
||||
@@ -1118,7 +1150,7 @@ func (b *blitter) blit(mat materialType, col f32color.RGBA, col1, col2 f32color.
|
||||
uniforms = &b.linearGradientUniforms.vert.blitUniforms
|
||||
}
|
||||
uniforms.transform = [4]float32{scale.X, scale.Y, off.X, off.Y}
|
||||
p.UploadUniforms()
|
||||
p.UploadUniforms(b.ctx)
|
||||
b.ctx.DrawArrays(driver.DrawModeTriangleStrip, 0, 4)
|
||||
}
|
||||
|
||||
@@ -1146,36 +1178,26 @@ func (u *uniformBuffer) Release() {
|
||||
u.buf = nil
|
||||
}
|
||||
|
||||
func newProgram(prog driver.Program, vertUniforms, fragUniforms *uniformBuffer) *program {
|
||||
if vertUniforms != nil {
|
||||
prog.SetVertexUniforms(vertUniforms.buf)
|
||||
}
|
||||
if fragUniforms != nil {
|
||||
prog.SetFragmentUniforms(fragUniforms.buf)
|
||||
}
|
||||
return &program{prog: prog, vertUniforms: vertUniforms, fragUniforms: fragUniforms}
|
||||
}
|
||||
|
||||
func (p *program) UploadUniforms() {
|
||||
func (p *pipeline) UploadUniforms(ctx driver.Device) {
|
||||
if p.vertUniforms != nil {
|
||||
ctx.BindVertexUniforms(p.vertUniforms.buf)
|
||||
p.vertUniforms.Upload()
|
||||
}
|
||||
if p.fragUniforms != nil {
|
||||
ctx.BindFragmentUniforms(p.fragUniforms.buf)
|
||||
p.fragUniforms.Upload()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *program) Release() {
|
||||
p.prog.Release()
|
||||
p.prog = nil
|
||||
func (p *pipeline) Release() {
|
||||
p.pipeline.Release()
|
||||
if p.vertUniforms != nil {
|
||||
p.vertUniforms.Release()
|
||||
p.vertUniforms = nil
|
||||
}
|
||||
if p.fragUniforms != nil {
|
||||
p.fragUniforms.Release()
|
||||
p.fragUniforms = nil
|
||||
}
|
||||
*p = pipeline{}
|
||||
}
|
||||
|
||||
// texSpaceTransform return the scale and offset that transforms the given subimage
|
||||
|
||||
Reference in New Issue
Block a user