graphicsdriver/metal: Add a generalized 'draw' function

This is a preparation for rendering shaders.

Updates #1165
This commit is contained in:
Hajime Hoshi 2020-08-08 03:01:23 +09:00
parent 28c638cb5b
commit fe1cea533a

View File

@ -641,13 +641,7 @@ func (g *Graphics) Reset() error {
return nil return nil
} }
func (g *Graphics) Draw(dstID, srcID driver.ImageID, indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address, sourceRegion driver.Region) error { func (g *Graphics) draw(rps mtl.RenderPipelineState, dst *Image, srcs [graphics.ShaderImageNum]*Image, indexLen int, indexOffset int, uniforms []interface{}) error {
// TODO: Use sourceRegion.
dst := g.images[dstID]
src := g.images[srcID]
if err := g.t.Call(func() error {
g.view.update() g.view.update()
rpd := mtl.RenderPassDescriptor{} rpd := mtl.RenderPassDescriptor{}
@ -676,18 +670,8 @@ func (g *Graphics) Draw(dstID, srcID driver.ImageID, indexLen int, indexOffset i
g.cb = g.cq.MakeCommandBuffer() g.cb = g.cq.MakeCommandBuffer()
} }
rce := g.cb.MakeRenderCommandEncoder(rpd) rce := g.cb.MakeRenderCommandEncoder(rpd)
rce.SetRenderPipelineState(rps)
if dst.screen && filter == driver.FilterScreen {
rce.SetRenderPipelineState(g.screenRPS)
} else {
rce.SetRenderPipelineState(g.rpss[rpsKey{
screen: dst.screen,
useColorM: colorM != nil,
filter: filter,
address: address,
compositeMode: mode,
}])
}
// In Metal, the NDC's Y direction (upward) and the framebuffer's Y direction (downward) don't // In Metal, the NDC's Y direction (upward) and the framebuffer's Y direction (downward) don't
// match. Then, the Y direction must be inverted. // match. Then, the Y direction must be inverted.
w, h := dst.viewportSize() w, h := dst.viewportSize()
@ -701,43 +685,80 @@ func (g *Graphics) Draw(dstID, srcID driver.ImageID, indexLen int, indexOffset i
}) })
rce.SetVertexBuffer(g.vb, 0, 0) rce.SetVertexBuffer(g.vb, 0, 0)
viewportSize := [...]float32{float32(w), float32(h)} for i, u := range uniforms {
rce.SetVertexBytes(unsafe.Pointer(&viewportSize[0]), unsafe.Sizeof(viewportSize), 1) switch u := u.(type) {
case float32:
sourceSize := [...]float32{ rce.SetVertexBytes(unsafe.Pointer(&u), unsafe.Sizeof(u), i+1)
float32(graphics.InternalImageSize(src.width)), rce.SetFragmentBytes(unsafe.Pointer(&u), unsafe.Sizeof(u), i+1)
float32(graphics.InternalImageSize(src.height)), case []float32:
rce.SetVertexBytes(unsafe.Pointer(&u[0]), unsafe.Sizeof(u[0])*uintptr(len(u)), i+1)
rce.SetFragmentBytes(unsafe.Pointer(&u[0]), unsafe.Sizeof(u[0])*uintptr(len(u)), i+1)
default:
return fmt.Errorf("metal: unexpected uniform value: %[1]v (type: %[1]T)", u)
}
} }
rce.SetFragmentBytes(unsafe.Pointer(&sourceSize[0]), unsafe.Sizeof(sourceSize), 2)
for i, src := range srcs {
if src != nil {
rce.SetFragmentTexture(src.texture, i)
} else {
rce.SetFragmentTexture(mtl.Texture{}, i)
}
}
rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, indexLen, mtl.IndexTypeUInt16, g.ib, indexOffset*2)
rce.EndEncoding()
return nil
}
func (g *Graphics) Draw(dstID, srcID driver.ImageID, indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address, sourceRegion driver.Region) error {
dst := g.images[dstID]
srcs := [graphics.ShaderImageNum]*Image{g.images[srcID]}
var rps mtl.RenderPipelineState
if dst.screen && filter == driver.FilterScreen {
rps = g.screenRPS
} else {
rps = g.rpss[rpsKey{
screen: dst.screen,
useColorM: colorM != nil,
filter: filter,
address: address,
compositeMode: mode,
}]
}
if err := g.t.Call(func() error {
w, h := dst.viewportSize()
sourceSize := []float32{0, 0}
if filter != driver.FilterNearest {
sourceSize[0] = float32(graphics.InternalImageSize(srcs[0].width))
sourceSize[1] = float32(graphics.InternalImageSize(srcs[0].height))
}
esBody, esTranslate := colorM.UnsafeElements() esBody, esTranslate := colorM.UnsafeElements()
rce.SetFragmentBytes(unsafe.Pointer(&esBody[0]), unsafe.Sizeof(esBody[0])*uintptr(len(esBody)), 3) scale := float32(0)
rce.SetFragmentBytes(unsafe.Pointer(&esTranslate[0]), unsafe.Sizeof(esTranslate[0])*uintptr(len(esTranslate)), 4) if filter == driver.FilterScreen {
scale = float32(dst.width) / float32(srcs[0].width)
scale := float32(dst.width) / float32(src.width) }
rce.SetFragmentBytes(unsafe.Pointer(&scale), unsafe.Sizeof(scale), 5) uniforms := []interface{}{
[]float32{float32(w), float32(h)},
sr := [...]float32{ sourceSize,
esBody,
esTranslate,
scale,
[]float32{
sourceRegion.X, sourceRegion.X,
sourceRegion.Y, sourceRegion.Y,
sourceRegion.X + sourceRegion.Width, sourceRegion.X + sourceRegion.Width,
sourceRegion.Y + sourceRegion.Height, sourceRegion.Y + sourceRegion.Height,
},
} }
rce.SetFragmentBytes(unsafe.Pointer(&sr[0]), unsafe.Sizeof(sr), 6) if err := g.draw(rps, dst, srcs, indexLen, indexOffset, uniforms); err != nil {
return err
if src != nil {
rce.SetFragmentTexture(src.texture, 0)
} else {
rce.SetFragmentTexture(mtl.Texture{}, 0)
} }
rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, indexLen, mtl.IndexTypeUInt16, g.ib, indexOffset*2)
rce.EndEncoding()
return nil return nil
}); err != nil { }); err != nil {
return err return err
} }
return nil return nil
} }