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
}
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 {
// TODO: Use sourceRegion.
dst := g.images[dstID]
src := g.images[srcID]
if err := g.t.Call(func() error {
func (g *Graphics) draw(rps mtl.RenderPipelineState, dst *Image, srcs [graphics.ShaderImageNum]*Image, indexLen int, indexOffset int, uniforms []interface{}) error {
g.view.update()
rpd := mtl.RenderPassDescriptor{}
@ -676,18 +670,8 @@ func (g *Graphics) Draw(dstID, srcID driver.ImageID, indexLen int, indexOffset i
g.cb = g.cq.MakeCommandBuffer()
}
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
// match. Then, the Y direction must be inverted.
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)
viewportSize := [...]float32{float32(w), float32(h)}
rce.SetVertexBytes(unsafe.Pointer(&viewportSize[0]), unsafe.Sizeof(viewportSize), 1)
sourceSize := [...]float32{
float32(graphics.InternalImageSize(src.width)),
float32(graphics.InternalImageSize(src.height)),
for i, u := range uniforms {
switch u := u.(type) {
case float32:
rce.SetVertexBytes(unsafe.Pointer(&u), unsafe.Sizeof(u), i+1)
rce.SetFragmentBytes(unsafe.Pointer(&u), unsafe.Sizeof(u), i+1)
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()
rce.SetFragmentBytes(unsafe.Pointer(&esBody[0]), unsafe.Sizeof(esBody[0])*uintptr(len(esBody)), 3)
rce.SetFragmentBytes(unsafe.Pointer(&esTranslate[0]), unsafe.Sizeof(esTranslate[0])*uintptr(len(esTranslate)), 4)
scale := float32(dst.width) / float32(src.width)
rce.SetFragmentBytes(unsafe.Pointer(&scale), unsafe.Sizeof(scale), 5)
sr := [...]float32{
scale := float32(0)
if filter == driver.FilterScreen {
scale = float32(dst.width) / float32(srcs[0].width)
}
uniforms := []interface{}{
[]float32{float32(w), float32(h)},
sourceSize,
esBody,
esTranslate,
scale,
[]float32{
sourceRegion.X,
sourceRegion.Y,
sourceRegion.X + sourceRegion.Width,
sourceRegion.Y + sourceRegion.Height,
},
}
rce.SetFragmentBytes(unsafe.Pointer(&sr[0]), unsafe.Sizeof(sr), 6)
if src != nil {
rce.SetFragmentTexture(src.texture, 0)
} else {
rce.SetFragmentTexture(mtl.Texture{}, 0)
if err := g.draw(rps, dst, srcs, indexLen, indexOffset, uniforms); err != nil {
return err
}
rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, indexLen, mtl.IndexTypeUInt16, g.ib, indexOffset*2)
rce.EndEncoding()
return nil
}); err != nil {
return err
}
return nil
}