From 2942f10d9d3ae2e784265e67bd6fe90e2531ac4f Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Tue, 13 Oct 2020 00:39:45 +0900 Subject: [PATCH] graphicsdriver/metal, graphicsdriver/opengl: Remove the thread usages for performance Instead, graphicscommand package has a thread. Updates #1367 --- internal/driver/graphics.go | 2 - internal/graphicscommand/command.go | 27 +- internal/graphicsdriver/metal/graphics.go | 629 ++++++++---------- internal/graphicsdriver/opengl/context.go | 3 - .../graphicsdriver/opengl/context_desktop.go | 574 ++++++---------- internal/graphicsdriver/opengl/context_js.go | 4 +- .../graphicsdriver/opengl/context_mobile.go | 4 +- internal/graphicsdriver/opengl/graphics.go | 5 - internal/graphicsdriver/opengl/image.go | 5 +- internal/uidriver/glfw/ui.go | 3 +- 10 files changed, 518 insertions(+), 738 deletions(-) diff --git a/internal/driver/graphics.go b/internal/driver/graphics.go index bbe5c709c..9330dda6a 100644 --- a/internal/driver/graphics.go +++ b/internal/driver/graphics.go @@ -20,7 +20,6 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/affine" "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/shaderir" - "github.com/hajimehoshi/ebiten/v2/internal/thread" ) type Region struct { @@ -31,7 +30,6 @@ type Region struct { } type Graphics interface { - SetThread(thread *thread.Thread) Begin() End() SetTransparent(transparent bool) diff --git a/internal/graphicscommand/command.go b/internal/graphicscommand/command.go index ef80c10eb..161498371 100644 --- a/internal/graphicscommand/command.go +++ b/internal/graphicscommand/command.go @@ -23,6 +23,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/driver" "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/shaderir" + "github.com/hajimehoshi/ebiten/v2/internal/thread" ) var theGraphicsDriver driver.Graphics @@ -31,6 +32,10 @@ func SetGraphicsDriver(driver driver.Graphics) { theGraphicsDriver = driver } +func SetThread(thread *thread.Thread) { + theCommandQueue.thread = thread +} + func NeedsRestoring() bool { if theGraphicsDriver == nil { // This happens on initialization. @@ -86,6 +91,8 @@ type commandQueue struct { tmpNumIndices int nextIndex int + thread *thread.Thread + err error } @@ -200,10 +207,19 @@ func (q *commandQueue) Enqueue(command command) { // Flush flushes the command queue. func (q *commandQueue) Flush() error { + return q.thread.Call(func() error { + return q.flush() + }) +} + +// flush must be called the main thread. +func (q *commandQueue) flush() error { if len(q.commands) == 0 { return nil } + // TODO: Use thread.Call here! + es := q.indices vs := q.vertices if recordLog() { @@ -716,10 +732,17 @@ func (c *newShaderCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [gra // ResetGraphicsDriverState resets or initializes the current graphics driver state. func ResetGraphicsDriverState() error { - return theGraphicsDriver.Reset() + return theCommandQueue.thread.Call(func() error { + return theGraphicsDriver.Reset() + }) } // MaxImageSize returns the maximum size of an image. func MaxImageSize() int { - return theGraphicsDriver.MaxImageSize() + var size int + _ = theCommandQueue.thread.Call(func() error { + size = theGraphicsDriver.MaxImageSize() + return nil + }) + return size } diff --git a/internal/graphicsdriver/metal/graphics.go b/internal/graphicsdriver/metal/graphics.go index 36cd0e655..e8799a190 100644 --- a/internal/graphicsdriver/metal/graphics.go +++ b/internal/graphicsdriver/metal/graphics.go @@ -27,7 +27,6 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/ca" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/mtl" "github.com/hajimehoshi/ebiten/v2/internal/shaderir" - "github.com/hajimehoshi/ebiten/v2/internal/thread" ) // #cgo CFLAGS: -x objective-c @@ -328,8 +327,6 @@ type Graphics struct { maxImageSize int tmpTexture mtl.Texture - t *thread.Thread - pool unsafe.Pointer } @@ -339,36 +336,23 @@ func Get() *Graphics { return &theGraphics } -func (g *Graphics) SetThread(thread *thread.Thread) { - g.t = thread -} - func (g *Graphics) Begin() { - g.t.Call(func() error { - // NSAutoreleasePool is required to release drawable correctly (#847). - // https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/Drawables.html - g.pool = C.allocAutoreleasePool() - return nil - }) + // NSAutoreleasePool is required to release drawable correctly (#847). + // https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/Drawables.html + g.pool = C.allocAutoreleasePool() } func (g *Graphics) End() { g.flushIfNeeded(false, true) - g.t.Call(func() error { - g.screenDrawable = ca.MetalDrawable{} - C.releaseAutoreleasePool(g.pool) - g.pool = nil - return nil - }) + g.screenDrawable = ca.MetalDrawable{} + C.releaseAutoreleasePool(g.pool) + g.pool = nil } func (g *Graphics) SetWindow(window uintptr) { - g.t.Call(func() error { - // Note that [NSApp mainWindow] returns nil when the window is borderless. - // Then the window is needed to be given explicitly. - g.view.setWindow(window) - return nil - }) + // Note that [NSApp mainWindow] returns nil when the window is borderless. + // Then the window is needed to be given explicitly. + g.view.setWindow(window) } func (g *Graphics) SetUIView(uiview uintptr) { @@ -377,37 +361,30 @@ func (g *Graphics) SetUIView(uiview uintptr) { } func (g *Graphics) SetVertices(vertices []float32, indices []uint16) { - g.t.Call(func() error { - if g.vb != (mtl.Buffer{}) { - g.vb.Release() - } - if g.ib != (mtl.Buffer{}) { - g.ib.Release() - } - g.vb = g.view.getMTLDevice().MakeBufferWithBytes(unsafe.Pointer(&vertices[0]), unsafe.Sizeof(vertices[0])*uintptr(len(vertices)), resourceStorageMode) - g.ib = g.view.getMTLDevice().MakeBufferWithBytes(unsafe.Pointer(&indices[0]), unsafe.Sizeof(indices[0])*uintptr(len(indices)), resourceStorageMode) - return nil - }) + if g.vb != (mtl.Buffer{}) { + g.vb.Release() + } + if g.ib != (mtl.Buffer{}) { + g.ib.Release() + } + g.vb = g.view.getMTLDevice().MakeBufferWithBytes(unsafe.Pointer(&vertices[0]), unsafe.Sizeof(vertices[0])*uintptr(len(vertices)), resourceStorageMode) + g.ib = g.view.getMTLDevice().MakeBufferWithBytes(unsafe.Pointer(&indices[0]), unsafe.Sizeof(indices[0])*uintptr(len(indices)), resourceStorageMode) } func (g *Graphics) flushIfNeeded(wait bool, present bool) { - g.t.Call(func() error { - if g.cb == (mtl.CommandBuffer{}) { - return nil - } + if g.cb == (mtl.CommandBuffer{}) { + return + } - if present && g.screenDrawable != (ca.MetalDrawable{}) { - g.cb.PresentDrawable(g.screenDrawable) - } - g.cb.Commit() - if wait { - g.cb.WaitUntilCompleted() - } + if present && g.screenDrawable != (ca.MetalDrawable{}) { + g.cb.PresentDrawable(g.screenDrawable) + } + g.cb.Commit() + if wait { + g.cb.WaitUntilCompleted() + } - g.cb = mtl.CommandBuffer{} - - return nil - }) + g.cb = mtl.CommandBuffer{} } func (g *Graphics) checkSize(width, height int) { @@ -452,11 +429,7 @@ func (g *Graphics) NewImage(width, height int) (driver.Image, error) { StorageMode: storageMode, Usage: mtl.TextureUsageShaderRead | mtl.TextureUsageRenderTarget, } - var t mtl.Texture - g.t.Call(func() error { - t = g.view.getMTLDevice().MakeTexture(td) - return nil - }) + t := g.view.getMTLDevice().MakeTexture(td) i := &Image{ id: g.genNextImageID(), graphics: g, @@ -469,10 +442,7 @@ func (g *Graphics) NewImage(width, height int) (driver.Image, error) { } func (g *Graphics) NewScreenFramebufferImage(width, height int) (driver.Image, error) { - g.t.Call(func() error { - g.view.setDrawableSize(width, height) - return nil - }) + g.view.setDrawableSize(width, height) i := &Image{ id: g.genNextImageID(), graphics: g, @@ -524,126 +494,120 @@ func operationToBlendFactor(c driver.Operation) mtl.BlendFactor { } func (g *Graphics) Reset() error { - if err := g.t.Call(func() error { - if g.cq != (mtl.CommandQueue{}) { - g.cq.Release() - g.cq = mtl.CommandQueue{} - } + if g.cq != (mtl.CommandQueue{}) { + g.cq.Release() + g.cq = mtl.CommandQueue{} + } - // TODO: Release existing rpss - if g.rpss == nil { - g.rpss = map[rpsKey]mtl.RenderPipelineState{} - } + // TODO: Release existing rpss + if g.rpss == nil { + g.rpss = map[rpsKey]mtl.RenderPipelineState{} + } - if err := g.view.reset(); err != nil { - return err - } - if g.transparent { - g.view.ml.SetOpaque(false) - } + if err := g.view.reset(); err != nil { + return err + } + if g.transparent { + g.view.ml.SetOpaque(false) + } - replaces := map[string]string{ - "{{.FilterNearest}}": fmt.Sprintf("%d", driver.FilterNearest), - "{{.FilterLinear}}": fmt.Sprintf("%d", driver.FilterLinear), - "{{.FilterScreen}}": fmt.Sprintf("%d", driver.FilterScreen), - "{{.AddressClampToZero}}": fmt.Sprintf("%d", driver.AddressClampToZero), - "{{.AddressRepeat}}": fmt.Sprintf("%d", driver.AddressRepeat), - "{{.AddressUnsafe}}": fmt.Sprintf("%d", driver.AddressUnsafe), - } - src := source - for k, v := range replaces { - src = strings.Replace(src, k, v, -1) - } + replaces := map[string]string{ + "{{.FilterNearest}}": fmt.Sprintf("%d", driver.FilterNearest), + "{{.FilterLinear}}": fmt.Sprintf("%d", driver.FilterLinear), + "{{.FilterScreen}}": fmt.Sprintf("%d", driver.FilterScreen), + "{{.AddressClampToZero}}": fmt.Sprintf("%d", driver.AddressClampToZero), + "{{.AddressRepeat}}": fmt.Sprintf("%d", driver.AddressRepeat), + "{{.AddressUnsafe}}": fmt.Sprintf("%d", driver.AddressUnsafe), + } + src := source + for k, v := range replaces { + src = strings.Replace(src, k, v, -1) + } - lib, err := g.view.getMTLDevice().MakeLibrary(src, mtl.CompileOptions{}) - if err != nil { - return err - } - vs, err := lib.MakeFunction("VertexShader") - if err != nil { - return err - } - fs, err := lib.MakeFunction( - fmt.Sprintf("FragmentShader_%d_%d_%d", 0, driver.FilterScreen, driver.AddressUnsafe)) - if err != nil { - return err - } - rpld := mtl.RenderPipelineDescriptor{ - VertexFunction: vs, - FragmentFunction: fs, - } - rpld.ColorAttachments[0].PixelFormat = g.view.colorPixelFormat() - rpld.ColorAttachments[0].BlendingEnabled = true - rpld.ColorAttachments[0].DestinationAlphaBlendFactor = mtl.BlendFactorZero - rpld.ColorAttachments[0].DestinationRGBBlendFactor = mtl.BlendFactorZero - rpld.ColorAttachments[0].SourceAlphaBlendFactor = mtl.BlendFactorOne - rpld.ColorAttachments[0].SourceRGBBlendFactor = mtl.BlendFactorOne - rps, err := g.view.getMTLDevice().MakeRenderPipelineState(rpld) - if err != nil { - return err - } - g.screenRPS = rps + lib, err := g.view.getMTLDevice().MakeLibrary(src, mtl.CompileOptions{}) + if err != nil { + return err + } + vs, err := lib.MakeFunction("VertexShader") + if err != nil { + return err + } + fs, err := lib.MakeFunction( + fmt.Sprintf("FragmentShader_%d_%d_%d", 0, driver.FilterScreen, driver.AddressUnsafe)) + if err != nil { + return err + } + rpld := mtl.RenderPipelineDescriptor{ + VertexFunction: vs, + FragmentFunction: fs, + } + rpld.ColorAttachments[0].PixelFormat = g.view.colorPixelFormat() + rpld.ColorAttachments[0].BlendingEnabled = true + rpld.ColorAttachments[0].DestinationAlphaBlendFactor = mtl.BlendFactorZero + rpld.ColorAttachments[0].DestinationRGBBlendFactor = mtl.BlendFactorZero + rpld.ColorAttachments[0].SourceAlphaBlendFactor = mtl.BlendFactorOne + rpld.ColorAttachments[0].SourceRGBBlendFactor = mtl.BlendFactorOne + rps, err := g.view.getMTLDevice().MakeRenderPipelineState(rpld) + if err != nil { + return err + } + g.screenRPS = rps - for _, screen := range []bool{false, true} { - for _, cm := range []bool{false, true} { - for _, a := range []driver.Address{ - driver.AddressClampToZero, - driver.AddressRepeat, - driver.AddressUnsafe, + for _, screen := range []bool{false, true} { + for _, cm := range []bool{false, true} { + for _, a := range []driver.Address{ + driver.AddressClampToZero, + driver.AddressRepeat, + driver.AddressUnsafe, + } { + for _, f := range []driver.Filter{ + driver.FilterNearest, + driver.FilterLinear, } { - for _, f := range []driver.Filter{ - driver.FilterNearest, - driver.FilterLinear, - } { - for c := driver.CompositeModeSourceOver; c <= driver.CompositeModeMax; c++ { - cmi := 0 - if cm { - cmi = 1 - } - fs, err := lib.MakeFunction(fmt.Sprintf("FragmentShader_%d_%d_%d", cmi, f, a)) - if err != nil { - return err - } - rpld := mtl.RenderPipelineDescriptor{ - VertexFunction: vs, - FragmentFunction: fs, - } - - pix := mtl.PixelFormatRGBA8UNorm - if screen { - pix = g.view.colorPixelFormat() - } - rpld.ColorAttachments[0].PixelFormat = pix - rpld.ColorAttachments[0].BlendingEnabled = true - - src, dst := c.Operations() - rpld.ColorAttachments[0].DestinationAlphaBlendFactor = operationToBlendFactor(dst) - rpld.ColorAttachments[0].DestinationRGBBlendFactor = operationToBlendFactor(dst) - rpld.ColorAttachments[0].SourceAlphaBlendFactor = operationToBlendFactor(src) - rpld.ColorAttachments[0].SourceRGBBlendFactor = operationToBlendFactor(src) - rps, err := g.view.getMTLDevice().MakeRenderPipelineState(rpld) - if err != nil { - return err - } - g.rpss[rpsKey{ - screen: screen, - useColorM: cm, - filter: f, - address: a, - compositeMode: c, - }] = rps + for c := driver.CompositeModeSourceOver; c <= driver.CompositeModeMax; c++ { + cmi := 0 + if cm { + cmi = 1 } + fs, err := lib.MakeFunction(fmt.Sprintf("FragmentShader_%d_%d_%d", cmi, f, a)) + if err != nil { + return err + } + rpld := mtl.RenderPipelineDescriptor{ + VertexFunction: vs, + FragmentFunction: fs, + } + + pix := mtl.PixelFormatRGBA8UNorm + if screen { + pix = g.view.colorPixelFormat() + } + rpld.ColorAttachments[0].PixelFormat = pix + rpld.ColorAttachments[0].BlendingEnabled = true + + src, dst := c.Operations() + rpld.ColorAttachments[0].DestinationAlphaBlendFactor = operationToBlendFactor(dst) + rpld.ColorAttachments[0].DestinationRGBBlendFactor = operationToBlendFactor(dst) + rpld.ColorAttachments[0].SourceAlphaBlendFactor = operationToBlendFactor(src) + rpld.ColorAttachments[0].SourceRGBBlendFactor = operationToBlendFactor(src) + rps, err := g.view.getMTLDevice().MakeRenderPipelineState(rpld) + if err != nil { + return err + } + g.rpss[rpsKey{ + screen: screen, + useColorM: cm, + filter: f, + address: a, + compositeMode: c, + }] = rps } } } } - - g.cq = g.view.getMTLDevice().MakeCommandQueue() - return nil - }); err != nil { - return err } + g.cq = g.view.getMTLDevice().MakeCommandQueue() return nil } @@ -733,37 +697,32 @@ func (g *Graphics) Draw(dstID, srcID driver.ImageID, indexLen int, indexOffset i }] } - if err := g.t.Call(func() error { - w, h := dst.internalSize() - sourceSize := []float32{0, 0} - if filter != driver.FilterNearest { - w, h := srcs[0].internalSize() - sourceSize[0] = float32(w) - sourceSize[1] = float32(h) - } - esBody, esTranslate := colorM.UnsafeElements() - 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, - }, - } - if err := g.draw(rps, dst, srcs, indexLen, indexOffset, uniforms); err != nil { - return err - } - return nil - }); err != nil { + w, h := dst.internalSize() + sourceSize := []float32{0, 0} + if filter != driver.FilterNearest { + w, h := srcs[0].internalSize() + sourceSize[0] = float32(w) + sourceSize[1] = float32(h) + } + esBody, esTranslate := colorM.UnsafeElements() + 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, + }, + } + if err := g.draw(rps, dst, srcs, indexLen, indexOffset, uniforms); err != nil { return err } return nil @@ -790,52 +749,40 @@ func (g *Graphics) HasHighPrecisionFloat() bool { } func (g *Graphics) MaxImageSize() int { - m := 0 - g.t.Call(func() error { - if g.maxImageSize == 0 { + if g.maxImageSize == 0 { + g.maxImageSize = 4096 + // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf + switch { + case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily5_v1): + g.maxImageSize = 16384 + case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily4_v1): + g.maxImageSize = 16384 + case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily3_v1): + g.maxImageSize = 16384 + case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily2_v2): + g.maxImageSize = 8192 + case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily2_v1): g.maxImageSize = 4096 - // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf - switch { - case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily5_v1): - g.maxImageSize = 16384 - case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily4_v1): - g.maxImageSize = 16384 - case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily3_v1): - g.maxImageSize = 16384 - case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily2_v2): - g.maxImageSize = 8192 - case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily2_v1): - g.maxImageSize = 4096 - case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily1_v2): - g.maxImageSize = 8192 - case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily1_v1): - g.maxImageSize = 4096 - case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_tvOS_GPUFamily2_v1): - g.maxImageSize = 16384 - case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_tvOS_GPUFamily1_v1): - g.maxImageSize = 8192 - case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_macOS_GPUFamily1_v1): - g.maxImageSize = 16384 - default: - panic("metal: there is no supported feature set") - } + case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily1_v2): + g.maxImageSize = 8192 + case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily1_v1): + g.maxImageSize = 4096 + case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_tvOS_GPUFamily2_v1): + g.maxImageSize = 16384 + case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_tvOS_GPUFamily1_v1): + g.maxImageSize = 8192 + case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_macOS_GPUFamily1_v1): + g.maxImageSize = 16384 + default: + panic("metal: there is no supported feature set") } - m = g.maxImageSize - return nil - }) - return m + } + return g.maxImageSize } func (g *Graphics) NewShader(program *shaderir.Program) (driver.Shader, error) { - var s *Shader - if err := g.t.Call(func() error { - var err error - s, err = newShader(g.view.getMTLDevice(), g.genNextShaderID(), program) - if err != nil { - return err - } - return nil - }); err != nil { + s, err := newShader(g.view.getMTLDevice(), g.genNextShaderID(), program) + if err != nil { return nil, err } g.addShader(s) @@ -877,13 +824,10 @@ func (i *Image) internalSize() (int, int) { } func (i *Image) Dispose() { - i.graphics.t.Call(func() error { - if i.texture != (mtl.Texture{}) { - i.texture.Release() - i.texture = mtl.Texture{} - } - return nil - }) + if i.texture != (mtl.Texture{}) { + i.texture.Release() + i.texture = mtl.Texture{} + } i.graphics.removeImage(i) } @@ -897,19 +841,16 @@ func (i *Image) IsInvalidated() bool { func (i *Image) syncTexture() { // Calling SynchronizeTexture is ignored on iOS (see mtl.m), but it looks like committing BliCommandEncoder // is necessary (#1337). - i.graphics.t.Call(func() error { - if i.graphics.cb != (mtl.CommandBuffer{}) { - panic("metal: command buffer must be empty at syncTexture: flushIfNeeded is not called yet?") - } + if i.graphics.cb != (mtl.CommandBuffer{}) { + panic("metal: command buffer must be empty at syncTexture: flushIfNeeded is not called yet?") + } - cb := i.graphics.cq.MakeCommandBuffer() - bce := cb.MakeBlitCommandEncoder() - bce.SynchronizeTexture(i.texture, 0, 0) - bce.EndEncoding() - cb.Commit() - cb.WaitUntilCompleted() - return nil - }) + cb := i.graphics.cq.MakeCommandBuffer() + bce := cb.MakeBlitCommandEncoder() + bce.SynchronizeTexture(i.texture, 0, 0) + bce.EndEncoding() + cb.Commit() + cb.WaitUntilCompleted() } func (i *Image) Pixels() ([]byte, error) { @@ -917,12 +858,9 @@ func (i *Image) Pixels() ([]byte, error) { i.syncTexture() b := make([]byte, 4*i.width*i.height) - i.graphics.t.Call(func() error { - i.texture.GetBytes(&b[0], uintptr(4*i.width), mtl.Region{ - Size: mtl.Size{Width: i.width, Height: i.height, Depth: 1}, - }, 0) - return nil - }) + i.texture.GetBytes(&b[0], uintptr(4*i.width), mtl.Region{ + Size: mtl.Size{Width: i.width, Height: i.height, Depth: 1}, + }, 0) return b, nil } @@ -932,58 +870,50 @@ func (i *Image) ReplacePixels(args []*driver.ReplacePixelsArgs) { // If the memory is shared (e.g., iOS), texture data doen't have to be synced. Send the data directly. if storageMode == mtl.StorageModeShared { - g.t.Call(func() error { - for _, a := range args { - i.texture.ReplaceRegion(mtl.Region{ - Origin: mtl.Origin{X: a.X, Y: a.Y, Z: 0}, - Size: mtl.Size{Width: a.Width, Height: a.Height, Depth: 1}, - }, 0, unsafe.Pointer(&a.Pixels[0]), 4*a.Width) - } - return nil - }) - return + for _, a := range args { + i.texture.ReplaceRegion(mtl.Region{ + Origin: mtl.Origin{X: a.X, Y: a.Y, Z: 0}, + Size: mtl.Size{Width: a.Width, Height: a.Height, Depth: 1}, + }, 0, unsafe.Pointer(&a.Pixels[0]), 4*a.Width) + } } // If the memory is managed (e.g., macOS), texture data cannot be sent to the destination directly because // this requires synchronizing data between CPU and GPU. As synchronizing is inefficient, let's send the // data to a temporary texture once, and then copy it in GPU. - g.t.Call(func() error { - w, h := i.texture.Width(), i.texture.Height() - if g.tmpTexture == (mtl.Texture{}) || w > g.tmpTexture.Width() || h > g.tmpTexture.Height() { - if g.tmpTexture != (mtl.Texture{}) { - g.tmpTexture.Release() - } - td := mtl.TextureDescriptor{ - TextureType: mtl.TextureType2D, - PixelFormat: mtl.PixelFormatRGBA8UNorm, - Width: w, - Height: h, - StorageMode: storageMode, - Usage: mtl.TextureUsageShaderRead | mtl.TextureUsageRenderTarget, - } - g.tmpTexture = g.view.getMTLDevice().MakeTexture(td) + w, h := i.texture.Width(), i.texture.Height() + if g.tmpTexture == (mtl.Texture{}) || w > g.tmpTexture.Width() || h > g.tmpTexture.Height() { + if g.tmpTexture != (mtl.Texture{}) { + g.tmpTexture.Release() } + td := mtl.TextureDescriptor{ + TextureType: mtl.TextureType2D, + PixelFormat: mtl.PixelFormatRGBA8UNorm, + Width: w, + Height: h, + StorageMode: storageMode, + Usage: mtl.TextureUsageShaderRead | mtl.TextureUsageRenderTarget, + } + g.tmpTexture = g.view.getMTLDevice().MakeTexture(td) + } - for _, a := range args { - g.tmpTexture.ReplaceRegion(mtl.Region{ - Origin: mtl.Origin{X: a.X, Y: a.Y, Z: 0}, - Size: mtl.Size{Width: a.Width, Height: a.Height, Depth: 1}, - }, 0, unsafe.Pointer(&a.Pixels[0]), 4*a.Width) - } + for _, a := range args { + g.tmpTexture.ReplaceRegion(mtl.Region{ + Origin: mtl.Origin{X: a.X, Y: a.Y, Z: 0}, + Size: mtl.Size{Width: a.Width, Height: a.Height, Depth: 1}, + }, 0, unsafe.Pointer(&a.Pixels[0]), 4*a.Width) + } - if g.cb == (mtl.CommandBuffer{}) { - g.cb = i.graphics.cq.MakeCommandBuffer() - } - bce := g.cb.MakeBlitCommandEncoder() - for _, a := range args { - o := mtl.Origin{X: a.X, Y: a.Y, Z: 0} - s := mtl.Size{Width: a.Width, Height: a.Height, Depth: 1} - bce.CopyFromTexture(g.tmpTexture, 0, 0, o, s, i.texture, 0, 0, o) - } - bce.EndEncoding() - - return nil - }) + if g.cb == (mtl.CommandBuffer{}) { + g.cb = i.graphics.cq.MakeCommandBuffer() + } + bce := g.cb.MakeBlitCommandEncoder() + for _, a := range args { + o := mtl.Origin{X: a.X, Y: a.Y, Z: 0} + s := mtl.Size{Width: a.Width, Height: a.Height, Depth: 1} + bce.CopyFromTexture(g.tmpTexture, 0, 0, o, s, i.texture, 0, 0, o) + } + bce.EndEncoding() } func (g *Graphics) DrawShader(dstID driver.ImageID, srcIDs [graphics.ShaderImageNum]driver.ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shader driver.ShaderID, indexLen int, indexOffset int, sourceRegion driver.Region, mode driver.CompositeMode, uniforms []interface{}) error { @@ -993,56 +923,51 @@ func (g *Graphics) DrawShader(dstID driver.ImageID, srcIDs [graphics.ShaderImage srcs[i] = g.images[srcID] } - if err := g.t.Call(func() error { - rps, err := g.shaders[shader].RenderPipelineState(g.view.getMTLDevice(), mode) - if err != nil { - return err + rps, err := g.shaders[shader].RenderPipelineState(g.view.getMTLDevice(), mode) + if err != nil { + return err + } + + us := make([]interface{}, graphics.PreservedUniformVariablesNum+len(uniforms)) + + // Set the destination texture size. + dw, dh := dst.internalSize() + us[graphics.DestinationTextureSizeUniformVariableIndex] = []float32{float32(dw), float32(dh)} + + // Set the source texture sizes. + usizes := make([]float32, 2*len(srcs)) + for i, src := range srcs { + if src != nil { + w, h := src.internalSize() + usizes[2*i] = float32(w) + usizes[2*i+1] = float32(h) } + } + us[graphics.TextureSizesUniformVariableIndex] = usizes - us := make([]interface{}, graphics.PreservedUniformVariablesNum+len(uniforms)) + // Set the source offsets. + uoffsets := make([]float32, 2*len(offsets)) + for i, offset := range offsets { + uoffsets[2*i] = offset[0] + uoffsets[2*i+1] = offset[1] + } + us[graphics.TextureSourceOffsetsUniformVariableIndex] = uoffsets - // Set the destination texture size. - dw, dh := dst.internalSize() - us[graphics.DestinationTextureSizeUniformVariableIndex] = []float32{float32(dw), float32(dh)} + // Set the source region's origin of texture0. + uorigin := []float32{float32(sourceRegion.X), float32(sourceRegion.Y)} + us[graphics.TextureSourceRegionOriginUniformVariableIndex] = uorigin - // Set the source texture sizes. - usizes := make([]float32, 2*len(srcs)) - for i, src := range srcs { - if src != nil { - w, h := src.internalSize() - usizes[2*i] = float32(w) - usizes[2*i+1] = float32(h) - } - } - us[graphics.TextureSizesUniformVariableIndex] = usizes + // Set the source region's size of texture0. + ussize := []float32{float32(sourceRegion.Width), float32(sourceRegion.Height)} + us[graphics.TextureSourceRegionSizeUniformVariableIndex] = ussize - // Set the source offsets. - uoffsets := make([]float32, 2*len(offsets)) - for i, offset := range offsets { - uoffsets[2*i] = offset[0] - uoffsets[2*i+1] = offset[1] - } - us[graphics.TextureSourceOffsetsUniformVariableIndex] = uoffsets + // Set the additional uniform variables. + for i, v := range uniforms { + const offset = graphics.PreservedUniformVariablesNum + us[offset+i] = v + } - // Set the source region's origin of texture0. - uorigin := []float32{float32(sourceRegion.X), float32(sourceRegion.Y)} - us[graphics.TextureSourceRegionOriginUniformVariableIndex] = uorigin - - // Set the source region's size of texture0. - ussize := []float32{float32(sourceRegion.Width), float32(sourceRegion.Height)} - us[graphics.TextureSourceRegionSizeUniformVariableIndex] = ussize - - // Set the additional uniform variables. - for i, v := range uniforms { - const offset = graphics.PreservedUniformVariablesNum - us[offset+i] = v - } - - if err := g.draw(rps, dst, srcs, indexLen, indexOffset, us); err != nil { - return err - } - return nil - }); err != nil { + if err := g.draw(rps, dst, srcs, indexLen, indexOffset, us); err != nil { return err } return nil diff --git a/internal/graphicsdriver/opengl/context.go b/internal/graphicsdriver/opengl/context.go index 2b70eb356..f5186970c 100644 --- a/internal/graphicsdriver/opengl/context.go +++ b/internal/graphicsdriver/opengl/context.go @@ -19,7 +19,6 @@ import ( "sync" "github.com/hajimehoshi/ebiten/v2/internal/driver" - "github.com/hajimehoshi/ebiten/v2/internal/thread" ) func convertOperation(op driver.Operation) operation { @@ -56,8 +55,6 @@ type context struct { highp bool highpOnce sync.Once - t *thread.Thread - contextImpl } diff --git a/internal/graphicsdriver/opengl/context_desktop.go b/internal/graphicsdriver/opengl/context_desktop.go index 31c63952f..4701dc8dc 100644 --- a/internal/graphicsdriver/opengl/context_desktop.go +++ b/internal/graphicsdriver/opengl/context_desktop.go @@ -101,131 +101,90 @@ type contextImpl struct { } func (c *context) reset() error { - if err := c.t.Call(func() error { - if c.init { - return nil - } + if !c.init { // Note that this initialization must be done after Loop is called. if err := gl.Init(); err != nil { return fmt.Errorf("opengl: initializing error %v", err) } c.init = true - return nil - }); err != nil { - return err } + c.locationCache = newLocationCache() c.lastTexture = invalidTexture c.lastFramebuffer = invalidFramebuffer c.lastViewportWidth = 0 c.lastViewportHeight = 0 c.lastCompositeMode = driver.CompositeModeUnknown - _ = c.t.Call(func() error { - gl.Enable(gl.BLEND) - return nil - }) + gl.Enable(gl.BLEND) + c.blendFunc(driver.CompositeModeSourceOver) - _ = c.t.Call(func() error { - f := int32(0) - gl.GetIntegerv(gl.FRAMEBUFFER_BINDING, &f) - c.screenFramebuffer = framebufferNative(f) - return nil - }) + + f := int32(0) + gl.GetIntegerv(gl.FRAMEBUFFER_BINDING, &f) + c.screenFramebuffer = framebufferNative(f) return nil } func (c *context) blendFunc(mode driver.CompositeMode) { - _ = c.t.Call(func() error { - if c.lastCompositeMode == mode { - return nil - } - c.lastCompositeMode = mode - s, d := mode.Operations() - s2, d2 := convertOperation(s), convertOperation(d) - gl.BlendFunc(uint32(s2), uint32(d2)) - return nil - }) + if c.lastCompositeMode == mode { + return + } + c.lastCompositeMode = mode + s, d := mode.Operations() + s2, d2 := convertOperation(s), convertOperation(d) + gl.BlendFunc(uint32(s2), uint32(d2)) } func (c *context) newTexture(width, height int) (textureNative, error) { - var texture textureNative - if err := c.t.Call(func() error { - var t uint32 - gl.GenTextures(1, &t) - // TODO: Use gl.IsTexture - if t <= 0 { - return errors.New("opengl: creating texture failed") - } - gl.PixelStorei(gl.UNPACK_ALIGNMENT, 4) - texture = textureNative(t) - return nil - }); err != nil { - return 0, err + var t uint32 + gl.GenTextures(1, &t) + // TODO: Use gl.IsTexture + if t <= 0 { + return 0, errors.New("opengl: creating texture failed") } + gl.PixelStorei(gl.UNPACK_ALIGNMENT, 4) + texture := textureNative(t) + c.bindTexture(texture) - _ = c.t.Call(func() error { - gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) - gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) - gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) - gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) - // If data is nil, this just allocates memory and the content is undefined. - // https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTexImage2D.xhtml - gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(width), int32(height), 0, gl.RGBA, gl.UNSIGNED_BYTE, nil) - return nil - }) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) + // If data is nil, this just allocates memory and the content is undefined. + // https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTexImage2D.xhtml + gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(width), int32(height), 0, gl.RGBA, gl.UNSIGNED_BYTE, nil) return texture, nil } func (c *context) bindFramebufferImpl(f framebufferNative) { - _ = c.t.Call(func() error { - gl.BindFramebufferEXT(gl.FRAMEBUFFER, uint32(f)) - return nil - }) + gl.BindFramebufferEXT(gl.FRAMEBUFFER, uint32(f)) } -func (c *context) framebufferPixels(f *framebuffer, width, height int) ([]byte, error) { - var pixels []byte - _ = c.t.Call(func() error { - gl.Flush() - return nil - }) +func (c *context) framebufferPixels(f *framebuffer, width, height int) []byte { + gl.Flush() c.bindFramebuffer(f.native) - if err := c.t.Call(func() error { - pixels = make([]byte, 4*width*height) - gl.ReadPixels(0, 0, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(pixels)) - return nil - }); err != nil { - return nil, err - } - return pixels, nil + pixels := make([]byte, 4*width*height) + gl.ReadPixels(0, 0, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(pixels)) + return pixels } func (c *context) activeTexture(idx int) { - _ = c.t.Call(func() error { - gl.ActiveTexture(gl.TEXTURE0 + uint32(idx)) - return nil - }) + gl.ActiveTexture(gl.TEXTURE0 + uint32(idx)) } func (c *context) bindTextureImpl(t textureNative) { - _ = c.t.Call(func() error { - gl.BindTexture(gl.TEXTURE_2D, uint32(t)) - return nil - }) + gl.BindTexture(gl.TEXTURE_2D, uint32(t)) } func (c *context) deleteTexture(t textureNative) { - _ = c.t.Call(func() error { - tt := uint32(t) - if !gl.IsTexture(tt) { - return nil - } - if c.lastTexture == t { - c.lastTexture = invalidTexture - } - gl.DeleteTextures(1, &tt) - return nil - }) + tt := uint32(t) + if !gl.IsTexture(tt) { + return + } + if c.lastTexture == t { + c.lastTexture = invalidTexture + } + gl.DeleteTextures(1, &tt) } func (c *context) isTexture(t textureNative) bool { @@ -233,155 +192,114 @@ func (c *context) isTexture(t textureNative) bool { } func (c *context) newFramebuffer(texture textureNative) (framebufferNative, error) { - var framebuffer framebufferNative var f uint32 - if err := c.t.Call(func() error { - gl.GenFramebuffersEXT(1, &f) - // TODO: Use gl.IsFramebuffer - if f <= 0 { - return errors.New("opengl: creating framebuffer failed: gl.IsFramebuffer returns false") - } - return nil - }); err != nil { - return 0, err + gl.GenFramebuffersEXT(1, &f) + // TODO: Use gl.IsFramebuffer + if f <= 0 { + return 0, errors.New("opengl: creating framebuffer failed: gl.IsFramebuffer returns false") } c.bindFramebuffer(framebufferNative(f)) - if err := c.t.Call(func() error { - gl.FramebufferTexture2DEXT(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, uint32(texture), 0) - s := gl.CheckFramebufferStatusEXT(gl.FRAMEBUFFER) - if s != gl.FRAMEBUFFER_COMPLETE { - if s != 0 { - return fmt.Errorf("opengl: creating framebuffer failed: %v", s) - } - if e := gl.GetError(); e != gl.NO_ERROR { - return fmt.Errorf("opengl: creating framebuffer failed: (glGetError) %d", e) - } - return fmt.Errorf("opengl: creating framebuffer failed: unknown error") + gl.FramebufferTexture2DEXT(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, uint32(texture), 0) + s := gl.CheckFramebufferStatusEXT(gl.FRAMEBUFFER) + if s != gl.FRAMEBUFFER_COMPLETE { + if s != 0 { + return 0, fmt.Errorf("opengl: creating framebuffer failed: %v", s) } - framebuffer = framebufferNative(f) - return nil - }); err != nil { - return 0, err + if e := gl.GetError(); e != gl.NO_ERROR { + return 0, fmt.Errorf("opengl: creating framebuffer failed: (glGetError) %d", e) + } + return 0, fmt.Errorf("opengl: creating framebuffer failed: unknown error") } - return framebuffer, nil + return framebufferNative(f), nil } func (c *context) setViewportImpl(width, height int) { - _ = c.t.Call(func() error { - gl.Viewport(0, 0, int32(width), int32(height)) - return nil - }) + gl.Viewport(0, 0, int32(width), int32(height)) } func (c *context) deleteFramebuffer(f framebufferNative) { - _ = c.t.Call(func() error { - ff := uint32(f) - if !gl.IsFramebufferEXT(ff) { - return nil - } - if c.lastFramebuffer == f { - c.lastFramebuffer = invalidFramebuffer - c.lastViewportWidth = 0 - c.lastViewportHeight = 0 - } - gl.DeleteFramebuffersEXT(1, &ff) - return nil - }) + ff := uint32(f) + if !gl.IsFramebufferEXT(ff) { + return + } + if c.lastFramebuffer == f { + c.lastFramebuffer = invalidFramebuffer + c.lastViewportWidth = 0 + c.lastViewportHeight = 0 + } + gl.DeleteFramebuffersEXT(1, &ff) } func (c *context) newShader(shaderType shaderType, source string) (shader, error) { - var sh shader - if err := c.t.Call(func() error { - s := gl.CreateShader(uint32(shaderType)) - if s == 0 { - return fmt.Errorf("opengl: glCreateShader failed: shader type: %d", shaderType) - } - cSources, free := gl.Strs(source + "\x00") - gl.ShaderSource(uint32(s), 1, cSources, nil) - free() - gl.CompileShader(s) - - var v int32 - gl.GetShaderiv(s, gl.COMPILE_STATUS, &v) - if v == gl.FALSE { - var l int32 - var log []byte - gl.GetShaderiv(uint32(s), gl.INFO_LOG_LENGTH, &l) - if l != 0 { - log = make([]byte, l) - gl.GetShaderInfoLog(s, l, nil, (*uint8)(gl.Ptr(log))) - } - return fmt.Errorf("opengl: shader compile failed: %s", log) - } - sh = shader(s) - return nil - }); err != nil { - return 0, err + s := gl.CreateShader(uint32(shaderType)) + if s == 0 { + return 0, fmt.Errorf("opengl: glCreateShader failed: shader type: %d", shaderType) } - return sh, nil + cSources, free := gl.Strs(source + "\x00") + gl.ShaderSource(uint32(s), 1, cSources, nil) + free() + gl.CompileShader(s) + + var v int32 + gl.GetShaderiv(s, gl.COMPILE_STATUS, &v) + if v == gl.FALSE { + var l int32 + var log []byte + gl.GetShaderiv(uint32(s), gl.INFO_LOG_LENGTH, &l) + if l != 0 { + log = make([]byte, l) + gl.GetShaderInfoLog(s, l, nil, (*uint8)(gl.Ptr(log))) + } + return 0, fmt.Errorf("opengl: shader compile failed: %s", log) + } + return shader(s), nil } func (c *context) deleteShader(s shader) { - _ = c.t.Call(func() error { - gl.DeleteShader(uint32(s)) - return nil - }) + gl.DeleteShader(uint32(s)) } func (c *context) newProgram(shaders []shader, attributes []string) (program, error) { - var pr program - if err := c.t.Call(func() error { - p := gl.CreateProgram() - if p == 0 { - return errors.New("opengl: glCreateProgram failed") - } - - for _, shader := range shaders { - gl.AttachShader(p, uint32(shader)) - } - - for i, name := range attributes { - l, free := gl.Strs(name + "\x00") - gl.BindAttribLocation(p, uint32(i), *l) - free() - } - - gl.LinkProgram(p) - var v int32 - gl.GetProgramiv(p, gl.LINK_STATUS, &v) - if v == gl.FALSE { - var l int32 - var log []byte - gl.GetProgramiv(p, gl.INFO_LOG_LENGTH, &l) - if l != 0 { - log = make([]byte, l) - gl.GetProgramInfoLog(p, l, nil, (*uint8)(gl.Ptr(log))) - } - return fmt.Errorf("opengl: program error: %s", log) - } - pr = program(p) - return nil - }); err != nil { - return 0, err + p := gl.CreateProgram() + if p == 0 { + return 0, errors.New("opengl: glCreateProgram failed") } - return pr, nil + + for _, shader := range shaders { + gl.AttachShader(p, uint32(shader)) + } + + for i, name := range attributes { + l, free := gl.Strs(name + "\x00") + gl.BindAttribLocation(p, uint32(i), *l) + free() + } + + gl.LinkProgram(p) + var v int32 + gl.GetProgramiv(p, gl.LINK_STATUS, &v) + if v == gl.FALSE { + var l int32 + var log []byte + gl.GetProgramiv(p, gl.INFO_LOG_LENGTH, &l) + if l != 0 { + log = make([]byte, l) + gl.GetProgramInfoLog(p, l, nil, (*uint8)(gl.Ptr(log))) + } + return 0, fmt.Errorf("opengl: program error: %s", log) + } + return program(p), nil } func (c *context) useProgram(p program) { - _ = c.t.Call(func() error { - gl.UseProgram(uint32(p)) - return nil - }) + gl.UseProgram(uint32(p)) } func (c *context) deleteProgram(p program) { - _ = c.t.Call(func() error { - if !gl.IsProgram(uint32(p)) { - return nil - } - gl.DeleteProgram(uint32(p)) - return nil - }) + if !gl.IsProgram(uint32(p)) { + return + } + gl.DeleteProgram(uint32(p)) } func (c *context) getUniformLocationImpl(p program, location string) uniformLocation { @@ -392,164 +310,110 @@ func (c *context) getUniformLocationImpl(p program, location string) uniformLoca } func (c *context) uniformInt(p program, location string, v int) bool { - var r bool - _ = c.t.Call(func() error { - l := int32(c.locationCache.GetUniformLocation(c, p, location)) - if l == invalidUniform { - return nil - } - r = true - gl.Uniform1i(l, int32(v)) - return nil - }) - return r + l := int32(c.locationCache.GetUniformLocation(c, p, location)) + if l == invalidUniform { + return false + } + gl.Uniform1i(l, int32(v)) + return true } func (c *context) uniformFloat(p program, location string, v float32) bool { - var r bool - _ = c.t.Call(func() error { - l := int32(c.locationCache.GetUniformLocation(c, p, location)) - if l == invalidUniform { - return nil - } - r = true - gl.Uniform1f(l, v) - return nil - }) - return r + l := int32(c.locationCache.GetUniformLocation(c, p, location)) + if l == invalidUniform { + return false + } + gl.Uniform1f(l, v) + return true } func (c *context) uniformFloats(p program, location string, v []float32, typ shaderir.Type) bool { - var r bool - _ = c.t.Call(func() error { - l := int32(c.locationCache.GetUniformLocation(c, p, location)) - if l == invalidUniform { - return nil - } - r = true + l := int32(c.locationCache.GetUniformLocation(c, p, location)) + if l == invalidUniform { + return false + } - base := typ.Main - len := int32(1) - if base == shaderir.Array { - base = typ.Sub[0].Main - len = int32(typ.Length) - } + base := typ.Main + len := int32(1) + if base == shaderir.Array { + base = typ.Sub[0].Main + len = int32(typ.Length) + } - switch base { - case shaderir.Float: - gl.Uniform1fv(l, len, (*float32)(gl.Ptr(v))) - case shaderir.Vec2: - gl.Uniform2fv(l, len, (*float32)(gl.Ptr(v))) - case shaderir.Vec3: - gl.Uniform3fv(l, len, (*float32)(gl.Ptr(v))) - case shaderir.Vec4: - gl.Uniform4fv(l, len, (*float32)(gl.Ptr(v))) - case shaderir.Mat2: - gl.UniformMatrix2fv(l, len, false, (*float32)(gl.Ptr(v))) - case shaderir.Mat3: - gl.UniformMatrix3fv(l, len, false, (*float32)(gl.Ptr(v))) - case shaderir.Mat4: - gl.UniformMatrix4fv(l, len, false, (*float32)(gl.Ptr(v))) - default: - panic(fmt.Sprintf("opengl: unexpected type: %s", typ.String())) - } - return nil - }) - return r + switch base { + case shaderir.Float: + gl.Uniform1fv(l, len, (*float32)(gl.Ptr(v))) + case shaderir.Vec2: + gl.Uniform2fv(l, len, (*float32)(gl.Ptr(v))) + case shaderir.Vec3: + gl.Uniform3fv(l, len, (*float32)(gl.Ptr(v))) + case shaderir.Vec4: + gl.Uniform4fv(l, len, (*float32)(gl.Ptr(v))) + case shaderir.Mat2: + gl.UniformMatrix2fv(l, len, false, (*float32)(gl.Ptr(v))) + case shaderir.Mat3: + gl.UniformMatrix3fv(l, len, false, (*float32)(gl.Ptr(v))) + case shaderir.Mat4: + gl.UniformMatrix4fv(l, len, false, (*float32)(gl.Ptr(v))) + default: + panic(fmt.Sprintf("opengl: unexpected type: %s", typ.String())) + } + return true } func (c *context) vertexAttribPointer(p program, index int, size int, dataType dataType, stride int, offset int) { - _ = c.t.Call(func() error { - gl.VertexAttribPointer(uint32(index), int32(size), uint32(dataType), false, int32(stride), uintptr(offset)) - return nil - }) + gl.VertexAttribPointer(uint32(index), int32(size), uint32(dataType), false, int32(stride), uintptr(offset)) } func (c *context) enableVertexAttribArray(p program, index int) { - _ = c.t.Call(func() error { - gl.EnableVertexAttribArray(uint32(index)) - return nil - }) + gl.EnableVertexAttribArray(uint32(index)) } func (c *context) disableVertexAttribArray(p program, index int) { - _ = c.t.Call(func() error { - gl.DisableVertexAttribArray(uint32(index)) - return nil - }) + gl.DisableVertexAttribArray(uint32(index)) } func (c *context) newArrayBuffer(size int) buffer { - var bf buffer - _ = c.t.Call(func() error { - var b uint32 - gl.GenBuffers(1, &b) - gl.BindBuffer(uint32(arrayBuffer), b) - gl.BufferData(uint32(arrayBuffer), size, nil, uint32(dynamicDraw)) - bf = buffer(b) - return nil - }) - return bf + var b uint32 + gl.GenBuffers(1, &b) + gl.BindBuffer(uint32(arrayBuffer), b) + gl.BufferData(uint32(arrayBuffer), size, nil, uint32(dynamicDraw)) + return buffer(b) } func (c *context) newElementArrayBuffer(size int) buffer { - var bf buffer - _ = c.t.Call(func() error { - var b uint32 - gl.GenBuffers(1, &b) - gl.BindBuffer(uint32(elementArrayBuffer), b) - gl.BufferData(uint32(elementArrayBuffer), size, nil, uint32(dynamicDraw)) - bf = buffer(b) - return nil - }) - return bf + var b uint32 + gl.GenBuffers(1, &b) + gl.BindBuffer(uint32(elementArrayBuffer), b) + gl.BufferData(uint32(elementArrayBuffer), size, nil, uint32(dynamicDraw)) + return buffer(b) } func (c *context) bindBuffer(bufferType bufferType, b buffer) { - _ = c.t.Call(func() error { - gl.BindBuffer(uint32(bufferType), uint32(b)) - return nil - }) + gl.BindBuffer(uint32(bufferType), uint32(b)) } func (c *context) arrayBufferSubData(data []float32) { - _ = c.t.Call(func() error { - gl.BufferSubData(uint32(arrayBuffer), 0, len(data)*4, gl.Ptr(data)) - return nil - }) + gl.BufferSubData(uint32(arrayBuffer), 0, len(data)*4, gl.Ptr(data)) } func (c *context) elementArrayBufferSubData(data []uint16) { - _ = c.t.Call(func() error { - gl.BufferSubData(uint32(elementArrayBuffer), 0, len(data)*2, gl.Ptr(data)) - return nil - }) + gl.BufferSubData(uint32(elementArrayBuffer), 0, len(data)*2, gl.Ptr(data)) } func (c *context) deleteBuffer(b buffer) { - _ = c.t.Call(func() error { - bb := uint32(b) - gl.DeleteBuffers(1, &bb) - return nil - }) + bb := uint32(b) + gl.DeleteBuffers(1, &bb) } func (c *context) drawElements(len int, offsetInBytes int) { - _ = c.t.Call(func() error { - gl.DrawElements(gl.TRIANGLES, int32(len), gl.UNSIGNED_SHORT, uintptr(offsetInBytes)) - return nil - }) + gl.DrawElements(gl.TRIANGLES, int32(len), gl.UNSIGNED_SHORT, uintptr(offsetInBytes)) } func (c *context) maxTextureSizeImpl() int { - size := 0 - _ = c.t.Call(func() error { - s := int32(0) - gl.GetIntegerv(gl.MAX_TEXTURE_SIZE, &s) - size = int(s) - return nil - }) - return size + s := int32(0) + gl.GetIntegerv(gl.MAX_TEXTURE_SIZE, &s) + return int(s) } func (c *context) getShaderPrecisionFormatPrecision() int { @@ -559,10 +423,7 @@ func (c *context) getShaderPrecisionFormatPrecision() int { } func (c *context) flush() { - _ = c.t.Call(func() error { - gl.Flush() - return nil - }) + gl.Flush() } func (c *context) needsRestoring() bool { @@ -570,54 +431,37 @@ func (c *context) needsRestoring() bool { } func (c *context) canUsePBO() bool { - var available bool - _ = c.t.Call(func() error { - available = isPBOAvailable() - return nil - }) - - return available + return isPBOAvailable() } func (c *context) texSubImage2D(t textureNative, width, height int, args []*driver.ReplacePixelsArgs) { c.bindTexture(t) - _ = c.t.Call(func() error { - for _, a := range args { - gl.TexSubImage2D(gl.TEXTURE_2D, 0, int32(a.X), int32(a.Y), int32(a.Width), int32(a.Height), gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(a.Pixels)) - } - return nil - }) + for _, a := range args { + gl.TexSubImage2D(gl.TEXTURE_2D, 0, int32(a.X), int32(a.Y), int32(a.Width), int32(a.Height), gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(a.Pixels)) + } } func (c *context) newPixelBufferObject(width, height int) buffer { - var bf buffer - _ = c.t.Call(func() error { - var b uint32 - gl.GenBuffers(1, &b) - gl.BindBuffer(gl.PIXEL_UNPACK_BUFFER, b) - gl.BufferData(gl.PIXEL_UNPACK_BUFFER, 4*width*height, nil, gl.STREAM_DRAW) - gl.BindBuffer(gl.PIXEL_UNPACK_BUFFER, 0) - bf = buffer(b) - return nil - }) - return bf + var b uint32 + gl.GenBuffers(1, &b) + gl.BindBuffer(gl.PIXEL_UNPACK_BUFFER, b) + gl.BufferData(gl.PIXEL_UNPACK_BUFFER, 4*width*height, nil, gl.STREAM_DRAW) + gl.BindBuffer(gl.PIXEL_UNPACK_BUFFER, 0) + return buffer(b) } func (c *context) replacePixelsWithPBO(buffer buffer, t textureNative, width, height int, args []*driver.ReplacePixelsArgs) { c.bindTexture(t) - _ = c.t.Call(func() error { - gl.BindBuffer(gl.PIXEL_UNPACK_BUFFER, uint32(buffer)) + gl.BindBuffer(gl.PIXEL_UNPACK_BUFFER, uint32(buffer)) - stride := 4 * width - for _, a := range args { - offset := 4 * (a.Y*width + a.X) - for j := 0; j < a.Height; j++ { - gl.BufferSubData(gl.PIXEL_UNPACK_BUFFER, offset+stride*j, 4*a.Width, gl.Ptr(a.Pixels[4*a.Width*j:4*a.Width*(j+1)])) - } + stride := 4 * width + for _, a := range args { + offset := 4 * (a.Y*width + a.X) + for j := 0; j < a.Height; j++ { + gl.BufferSubData(gl.PIXEL_UNPACK_BUFFER, offset+stride*j, 4*a.Width, gl.Ptr(a.Pixels[4*a.Width*j:4*a.Width*(j+1)])) } + } - gl.TexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, nil) - gl.BindBuffer(gl.PIXEL_UNPACK_BUFFER, 0) - return nil - }) + gl.TexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, nil) + gl.BindBuffer(gl.PIXEL_UNPACK_BUFFER, 0) } diff --git a/internal/graphicsdriver/opengl/context_js.go b/internal/graphicsdriver/opengl/context_js.go index ae7361100..676b5d13f 100644 --- a/internal/graphicsdriver/opengl/context_js.go +++ b/internal/graphicsdriver/opengl/context_js.go @@ -273,7 +273,7 @@ func (c *context) bindFramebufferImpl(f framebufferNative) { gl.Call("bindFramebuffer", framebuffer_, js.Value(f)) } -func (c *context) framebufferPixels(f *framebuffer, width, height int) ([]byte, error) { +func (c *context) framebufferPixels(f *framebuffer, width, height int) []byte { c.ensureGL() gl := c.gl @@ -282,7 +282,7 @@ func (c *context) framebufferPixels(f *framebuffer, width, height int) ([]byte, p := jsutil.TemporaryUint8Array(4 * width * height) gl.Call("readPixels", 0, 0, width, height, rgba, unsignedByte, p) - return jsutil.Uint8ArrayToSlice(p), nil + return jsutil.Uint8ArrayToSlice(p) } func (c *context) activeTexture(idx int) { diff --git a/internal/graphicsdriver/opengl/context_mobile.go b/internal/graphicsdriver/opengl/context_mobile.go index 1cdb4023d..b7b6a43ab 100644 --- a/internal/graphicsdriver/opengl/context_mobile.go +++ b/internal/graphicsdriver/opengl/context_mobile.go @@ -148,7 +148,7 @@ func (c *context) bindFramebufferImpl(f framebufferNative) { gl.BindFramebuffer(mgl.FRAMEBUFFER, mgl.Framebuffer(f)) } -func (c *context) framebufferPixels(f *framebuffer, width, height int) ([]byte, error) { +func (c *context) framebufferPixels(f *framebuffer, width, height int) []byte { gl := c.gl gl.Flush() @@ -156,7 +156,7 @@ func (c *context) framebufferPixels(f *framebuffer, width, height int) ([]byte, pixels := make([]byte, 4*width*height) gl.ReadPixels(pixels, 0, 0, width, height, mgl.RGBA, mgl.UNSIGNED_BYTE) - return pixels, nil + return pixels } func (c *context) activeTexture(idx int) { diff --git a/internal/graphicsdriver/opengl/graphics.go b/internal/graphicsdriver/opengl/graphics.go index fb6e15179..029cd732e 100644 --- a/internal/graphicsdriver/opengl/graphics.go +++ b/internal/graphicsdriver/opengl/graphics.go @@ -21,7 +21,6 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/driver" "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/shaderir" - "github.com/hajimehoshi/ebiten/v2/internal/thread" ) var theGraphics Graphics @@ -44,10 +43,6 @@ type Graphics struct { drawCalled bool } -func (g *Graphics) SetThread(thread *thread.Thread) { - g.context.t = thread -} - func (g *Graphics) Begin() { // Do nothing. } diff --git a/internal/graphicsdriver/opengl/image.go b/internal/graphicsdriver/opengl/image.go index c4abea793..ae73d15b2 100644 --- a/internal/graphicsdriver/opengl/image.go +++ b/internal/graphicsdriver/opengl/image.go @@ -64,10 +64,7 @@ func (i *Image) Pixels() ([]byte, error) { if err := i.ensureFramebuffer(); err != nil { return nil, err } - p, err := i.graphics.context.framebufferPixels(i.framebuffer, i.width, i.height) - if err != nil { - return nil, err - } + p := i.graphics.context.framebufferPixels(i.framebuffer, i.width, i.height) return p, nil } diff --git a/internal/uidriver/glfw/ui.go b/internal/uidriver/glfw/ui.go index 6a74f3681..9669b4f75 100644 --- a/internal/uidriver/glfw/ui.go +++ b/internal/uidriver/glfw/ui.go @@ -29,6 +29,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/devicescale" "github.com/hajimehoshi/ebiten/v2/internal/driver" "github.com/hajimehoshi/ebiten/v2/internal/glfw" + "github.com/hajimehoshi/ebiten/v2/internal/graphicscommand" "github.com/hajimehoshi/ebiten/v2/internal/hooks" "github.com/hajimehoshi/ebiten/v2/internal/thread" ) @@ -588,7 +589,7 @@ func (u *UserInterface) Run(uicontext driver.UIContext) error { // Initialize the main thread first so the thread is available at u.run (#809). u.t = thread.New() - u.Graphics().SetThread(u.t) + graphicscommand.SetThread(u.t) ch := make(chan error, 1) go func() {