// SPDX-License-Identifier: Unlicense OR MIT // Device interface and types. OpenGL only - no D3D11/Metal/Vulkan. package gio import ( "fmt" "image" ) // API is the GPU API discriminator. type API interface { implementsAPI() } // RenderTarget is implemented by framebuffer targets. type RenderTarget interface { ImplementsRenderTarget() } // OpenGLRenderTarget wraps a Framebuffer as a render target. type OpenGLRenderTarget Framebuffer func (OpenGLRenderTarget) ImplementsRenderTarget() {} // OpenGL holds the parameters to create an OpenGL device. type OpenGL struct { // Context is the WebGL context handle for WASM platforms. Context GLContext } func (OpenGL) implementsAPI() {} // NewDevice creates a Device for the given API. func NewDevice(api API) (Device, error) { switch a := api.(type) { case OpenGL: return newOpenGLDevice(a) } return nil, fmt.Errorf("gio: no driver available for the API %T", api) } // Device abstracts GPU operations. type Device interface { BeginFrame(target RenderTarget, clear bool, viewport image.Point) Texture EndFrame() Caps() Caps NewTimer() Timer IsTimeContinuous() bool NewTexture(format TextureFormat, width, height int, minFilter, magFilter TextureFilter, bindings BufferBinding) (Texture, error) NewImmutableBuffer(typ BufferBinding, data []byte) (Buffer, error) NewBuffer(typ BufferBinding, size int) (Buffer, error) NewComputeProgram(shader ShaderSources) (ShaderProgram, error) NewVertexShader(src ShaderSources) (VertexShader, error) NewFragmentShader(src ShaderSources) (FragmentShader, error) NewPipeline(desc PipelineDesc) (Pipeline, error) Viewport(x, y, width, height int) DrawArrays(off, count int) DrawElements(off, count int) BeginRenderPass(t Texture, desc LoadDesc) EndRenderPass() PrepareTexture(t Texture) BindProgram(p ShaderProgram) BindPipeline(p Pipeline) BindTexture(unit int, t Texture) BindVertexBuffer(b Buffer, offset int) BindIndexBuffer(b Buffer) BindImageTexture(unit int, texture Texture) BindUniforms(buf Buffer) BindStorageBuffer(binding int, buf Buffer) BeginCompute() EndCompute() CopyTexture(dst Texture, dstOrigin image.Point, src Texture, srcRect image.Rectangle) DispatchCompute(x, y, z int) Release() } type errorString string func (e errorString) Error() string { return string(e) } const ErrDeviceLost = errorString("GPU device lost") const ErrContentLost = errorString("buffer content lost") type LoadDesc struct { Action LoadAction ClearColor RGBA } type Pipeline interface { Release() } type PipelineDesc struct { VertexShader VertexShader FragmentShader FragmentShader VertexLayout VertexLayout BlendDesc BlendDesc PixelFormat TextureFormat Topology Topology } type VertexLayout struct { Inputs []InputDesc Stride int } type InputDesc struct { Type ShaderDataType Size int Offset int } type BlendDesc struct { Enable bool SrcFactor, DstFactor BlendFactor } type BlendFactor uint8 type Topology uint8 type ( TextureFilter uint8 TextureFormat uint8 ) type BufferBinding uint8 type LoadAction uint8 type Features uint32 type Caps struct { BottomLeftOrigin bool Features Features MaxTextureSize int } type VertexShader interface { Release() } type FragmentShader interface { Release() } type ShaderProgram interface { Release() } type Buffer interface { Release() Upload(data []byte) Download(data []byte) error } type Timer interface { Begin() End() Release() } type Texture interface { RenderTarget Upload(offset, size image.Point, pixels []byte, stride int) ReadPixels(src image.Rectangle, pixels []byte, stride int) error Release() } const ( BufferBindingIndices BufferBinding = 1 << iota BufferBindingVertices BufferBindingUniforms BufferBindingTexture BufferBindingFramebuffer BufferBindingShaderStorageRead BufferBindingShaderStorageWrite ) const ( TextureFormatSRGBA TextureFormat = iota TextureFormatFloat TextureFormatRGBA8 TextureFormatOutput ) const ( FilterNearest TextureFilter = iota FilterLinear FilterLinearMipmapLinear ) const ( FeatureTimers Features = 1 << iota FeatureFloatRenderTargets FeatureCompute FeatureSRGB ) const ( TopologyTriangleStrip Topology = iota TopologyTriangles ) const ( BlendFactorOne BlendFactor = iota BlendFactorOneMinusSrcAlpha BlendFactorZero BlendFactorDstColor ) const ( LoadActionKeep LoadAction = iota LoadActionClear LoadActionInvalidate ) func (f Features) Has(feats Features) bool { return f&feats == feats } func DownloadImage(d Device, t Texture, img *image.RGBA) error { r := img.Bounds() if err := t.ReadPixels(r, img.Pix, img.Stride); err != nil { return err } if d.Caps().BottomLeftOrigin { flipImageY(r.Dx()*4, r.Dy(), img.Pix) } return nil } func flipImageY(stride, height int, pixels []byte) { row := []byte{:stride} for y := 0; y < height/2; y++ { y1 := height - y - 1 dest := y1 * stride src := y * stride copy(row, pixels[dest:]) copy(pixels[dest:], pixels[src:src+len(row)]) copy(pixels[src:], row) } } func UploadImage(t Texture, offset image.Point, img *image.RGBA) { size := img.Bounds().Size() min := img.Rect.Min start := img.PixOffset(min.X, min.Y) end := img.PixOffset(min.X+size.X, min.Y+size.Y-1) t.Upload(offset, size, img.Pix[start:end], img.Stride) }