forked from joejulian/gio
gpu: introduce driver.Device.PrepareTexture and use it in renderers
Vulkan textures (VkImage) are always in a particular layout, where each layout is optimized for a particular use (transfer, sampling, compute storage). Vulkan allows layout transitions everywhere except inside render passes. This change adds driver.Device.PrepareTexture for instructing the driver to switch a texture to a layout for sampling in preparation for using it in a render pass. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
@@ -10,5 +10,5 @@ require (
|
||||
|
||||
require (
|
||||
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2
|
||||
gioui.org/shader v1.0.3
|
||||
gioui.org/shader v1.0.4
|
||||
)
|
||||
|
||||
@@ -4,8 +4,8 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7
|
||||
gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
|
||||
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 h1:AGDDxsJE1RpcXTAxPG2B4jrwVUJGFDjINIPi1jtO6pc=
|
||||
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
|
||||
gioui.org/shader v1.0.3 h1:xllhh4w2XWej9lfkHMtOtI3ES05T8xZzxrSSWbMZgTw=
|
||||
gioui.org/shader v1.0.3/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM=
|
||||
gioui.org/shader v1.0.4 h1:taS5l4MYdnKOQ5cRh1FPUaOp0YAs0qkRrhwbZqHSYrQ=
|
||||
gioui.org/shader v1.0.4/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
|
||||
+5
-7
@@ -862,6 +862,7 @@ func (g *compute) blitLayers(d driver.LoadDesc, fbo driver.Framebuffer, viewport
|
||||
{posX: float32(r.Min.X), posY: float32(r.Max.Y), u: placef.X, v: placef.Y + sizef.Y},
|
||||
}
|
||||
g.output.layerVertices = append(g.output.layerVertices, quad[0], quad[1], quad[3], quad[3], quad[2], quad[1])
|
||||
g.ctx.PrepareTexture(l.alloc.atlas.image)
|
||||
}
|
||||
if len(g.output.layerVertices) > 0 {
|
||||
vertexData := byteslice.Slice(g.output.layerVertices)
|
||||
@@ -893,9 +894,6 @@ func (g *compute) blitLayers(d driver.LoadDesc, fbo driver.Framebuffer, viewport
|
||||
// Transform positions to clip space: [-1, -1] - [1, 1], and texture
|
||||
// coordinates to texture space: [0, 0] - [1, 1].
|
||||
clip := f32.Affine2D{}.Scale(f32.Pt(0, 0), f32.Pt(2/float32(viewport.X), 2/float32(viewport.Y))).Offset(f32.Pt(-1, -1))
|
||||
// Flip y-axis to match framebuffer output space.
|
||||
flipY := f32.Affine2D{}.Scale(f32.Pt(0, 0), f32.Pt(1, -1)).Offset(f32.Pt(0, float32(viewport.Y)))
|
||||
clip = clip.Mul(flipY)
|
||||
sx, _, ox, _, sy, oy := clip.Elems()
|
||||
g.output.uniforms.scale = [2]float32{sx, sy}
|
||||
g.output.uniforms.pos = [2]float32{ox, oy}
|
||||
@@ -992,11 +990,10 @@ func (g *compute) renderMaterials() error {
|
||||
if err := g.realizeAtlas(atlas, g.useCPU, atlas.packer.sizes[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
// Transform to clip space: [-1, -1] - [1, 1] and flip Y-axis to cancel the implied transformation
|
||||
// between framebuffer and texture space.
|
||||
// Transform to clip space: [-1, -1] - [1, 1].
|
||||
*m.uniforms.u = materialUniforms{
|
||||
scale: [2]float32{2, -2},
|
||||
pos: [2]float32{-1, +1},
|
||||
scale: [2]float32{2, 2},
|
||||
pos: [2]float32{-1, -1},
|
||||
}
|
||||
if !g.srgb {
|
||||
m.uniforms.u.emulatesRGB = 1.0
|
||||
@@ -1010,6 +1007,7 @@ func (g *compute) renderMaterials() error {
|
||||
if !realized {
|
||||
d.Action = driver.LoadActionClear
|
||||
}
|
||||
g.ctx.PrepareTexture(imgAtlas.image)
|
||||
g.ctx.BeginRenderPass(atlas.fbo, d)
|
||||
g.ctx.BindTexture(0, imgAtlas.image)
|
||||
g.ctx.BindPipeline(m.pipeline)
|
||||
|
||||
+42
-11
@@ -289,7 +289,6 @@ type blitColUniforms struct {
|
||||
|
||||
type blitTexUniforms struct {
|
||||
blitUniforms
|
||||
_ [12]byte // Padding to 16 bytes.
|
||||
}
|
||||
|
||||
type blitLinearGradientUniforms struct {
|
||||
@@ -312,7 +311,6 @@ type blitUniforms struct {
|
||||
transform [4]float32
|
||||
uvTransformR1 [4]float32
|
||||
uvTransformR2 [4]float32
|
||||
z float32
|
||||
}
|
||||
|
||||
type colorUniforms struct {
|
||||
@@ -417,10 +415,12 @@ func (g *gpu) frame(target RenderTarget) error {
|
||||
g.renderer.packStencils(&g.drawOps.pathOps)
|
||||
g.renderer.stencilClips(g.drawOps.pathCache, g.drawOps.pathOps)
|
||||
g.renderer.packIntersections(g.drawOps.imageOps)
|
||||
g.renderer.prepareIntersections(g.drawOps.imageOps)
|
||||
g.renderer.intersect(g.drawOps.imageOps)
|
||||
g.stencilTimer.end()
|
||||
g.coverTimer.begin()
|
||||
g.renderer.uploadImages(g.cache, g.drawOps.imageOps)
|
||||
g.renderer.prepareDrawOps(g.cache, g.drawOps.imageOps)
|
||||
d := driver.LoadDesc{
|
||||
ClearColor: g.drawOps.clearColor,
|
||||
}
|
||||
@@ -508,10 +508,10 @@ func (r *renderer) release() {
|
||||
func newBlitter(ctx driver.Device) *blitter {
|
||||
quadVerts, err := ctx.NewImmutableBuffer(driver.BufferBindingVertices,
|
||||
byteslice.Slice([]float32{
|
||||
-1, +1, 0, 0,
|
||||
+1, +1, 1, 0,
|
||||
-1, -1, 0, 1,
|
||||
+1, -1, 1, 1,
|
||||
-1, -1, 0, 0,
|
||||
+1, -1, 1, 0,
|
||||
-1, +1, 0, 1,
|
||||
+1, +1, 1, 1,
|
||||
}),
|
||||
)
|
||||
if err != nil {
|
||||
@@ -669,6 +669,16 @@ func (r *renderer) stencilClips(pathCache *opCache, ops []*pathOp) {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *renderer) prepareIntersections(ops []imageOp) {
|
||||
for _, img := range ops {
|
||||
if img.clipType != clipTypeIntersection {
|
||||
continue
|
||||
}
|
||||
fbo := r.pather.stenciler.cover(img.path.place.Idx)
|
||||
r.ctx.PrepareTexture(fbo.tex)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *renderer) intersect(ops []imageOp) {
|
||||
if len(r.intersections.sizes) == 0 {
|
||||
return
|
||||
@@ -1084,6 +1094,27 @@ func (r *renderer) uploadImages(cache *resourceCache, ops []imageOp) {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *renderer) prepareDrawOps(cache *resourceCache, ops []imageOp) {
|
||||
for _, img := range ops {
|
||||
m := img.material
|
||||
switch m.material {
|
||||
case materialTexture:
|
||||
r.ctx.PrepareTexture(r.texHandle(cache, m.data))
|
||||
}
|
||||
|
||||
var fbo stencilFBO
|
||||
switch img.clipType {
|
||||
case clipTypeNone:
|
||||
continue
|
||||
case clipTypePath:
|
||||
fbo = r.pather.stenciler.cover(img.place.Idx)
|
||||
case clipTypeIntersection:
|
||||
fbo = r.pather.stenciler.intersections.fbos[img.place.Idx]
|
||||
}
|
||||
r.ctx.PrepareTexture(fbo.tex)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *renderer) drawOps(cache *resourceCache, ops []imageOp) {
|
||||
var coverTex driver.Texture
|
||||
for _, img := range ops {
|
||||
@@ -1216,25 +1247,25 @@ func gradientSpaceTransform(clip image.Rectangle, off f32.Point, stop1, stop2 f3
|
||||
}
|
||||
|
||||
// clipSpaceTransform returns the scale and offset that transforms the given
|
||||
// rectangle from a viewport into OpenGL clip space.
|
||||
// rectangle from a viewport into GPU driver device coordinates.
|
||||
func clipSpaceTransform(r image.Rectangle, viewport image.Point) (f32.Point, f32.Point) {
|
||||
// First, transform UI coordinates to OpenGL coordinates:
|
||||
// First, transform UI coordinates to device coordinates:
|
||||
//
|
||||
// [(-1, +1) (+1, +1)]
|
||||
// [(-1, -1) (+1, -1)]
|
||||
// [(-1, +1) (+1, +1)]
|
||||
//
|
||||
x, y := float32(r.Min.X), float32(r.Min.Y)
|
||||
w, h := float32(r.Dx()), float32(r.Dy())
|
||||
vx, vy := 2/float32(viewport.X), 2/float32(viewport.Y)
|
||||
x = x*vx - 1
|
||||
y = 1 - y*vy
|
||||
y = y*vy - 1
|
||||
w *= vx
|
||||
h *= vy
|
||||
|
||||
// Then, compute the transformation from the fullscreen quad to
|
||||
// the rectangle at (x, y) and dimensions (w, h).
|
||||
scale := f32.Point{X: w * .5, Y: h * .5}
|
||||
offset := f32.Point{X: x + w*.5, Y: y - h*.5}
|
||||
offset := f32.Point{X: x + w*.5, Y: y + h*.5}
|
||||
|
||||
return scale, offset
|
||||
}
|
||||
|
||||
@@ -75,9 +75,9 @@ func TestInputShader(t *testing.T) {
|
||||
defer pipe.Release()
|
||||
buf, err := b.NewImmutableBuffer(driver.BufferBindingVertices,
|
||||
byteslice.Slice([]float32{
|
||||
0, .5, .5, 1,
|
||||
-.5, -.5, .5, 1,
|
||||
.5, -.5, .5, 1,
|
||||
0, -.5, .5, 1,
|
||||
-.5, +.5, .5, 1,
|
||||
.5, +.5, .5, 1,
|
||||
}),
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -633,6 +633,8 @@ func (t *Texture) Release() {
|
||||
*t = Texture{}
|
||||
}
|
||||
|
||||
func (b *Backend) PrepareTexture(tex driver.Texture) {}
|
||||
|
||||
func (b *Backend) BindTexture(unit int, tex driver.Texture) {
|
||||
t := tex.(*Texture)
|
||||
b.ctx.PSSetSamplers(uint32(unit), t.sampler)
|
||||
|
||||
@@ -37,6 +37,7 @@ type Device interface {
|
||||
|
||||
BeginRenderPass(f Framebuffer, desc LoadDesc)
|
||||
EndRenderPass()
|
||||
PrepareTexture(t Texture)
|
||||
BindProgram(p Program)
|
||||
BindPipeline(p Pipeline)
|
||||
BindTexture(unit int, t Texture)
|
||||
|
||||
@@ -931,6 +931,8 @@ func (p *Pipeline) Release() {
|
||||
*p = Pipeline{}
|
||||
}
|
||||
|
||||
func (b *Backend) PrepareTexture(tex driver.Texture) {}
|
||||
|
||||
func (b *Backend) BindTexture(unit int, tex driver.Texture) {
|
||||
t := tex.(*Texture)
|
||||
if enc := b.renderEnc; enc != 0 {
|
||||
|
||||
@@ -1202,6 +1202,8 @@ func toTexFilter(f driver.TextureFilter) int {
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Backend) PrepareTexture(tex driver.Texture) {}
|
||||
|
||||
func (b *Backend) BindTexture(unit int, t driver.Texture) {
|
||||
b.glstate.bindTexture(b.funcs, unit, t.(*texture).obj)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user