github.com/racerxdl/gonx@v0.0.0-20210103083128-c5afc43bcbd2/services/display/surface.go (about)

     1  package display
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/racerxdl/gonx/nx/graphics"
     6  	"github.com/racerxdl/gonx/nx/memory"
     7  	"github.com/racerxdl/gonx/nx/nxerrors"
     8  	"github.com/racerxdl/gonx/nx/nxtypes"
     9  	"github.com/racerxdl/gonx/services/am"
    10  	"github.com/racerxdl/gonx/services/gpu"
    11  	"github.com/racerxdl/gonx/services/nv"
    12  	"github.com/racerxdl/gonx/services/vi"
    13  	"github.com/racerxdl/gonx/svc"
    14  	"image"
    15  	"unsafe"
    16  )
    17  
    18  // SurfaceState Keeps track of the internal state of a \ref surface_t
    19  type SurfaceState int
    20  
    21  const (
    22  	SURFACE_STATE_INVALID  SurfaceState = iota
    23  	SURFACE_STATE_DEQUEUED SurfaceState = iota
    24  	SURFACE_STATE_QUEUED   SurfaceState = iota
    25  )
    26  
    27  type Surface struct {
    28  	LayerId         uint64
    29  	IGBP            vi.IGBP
    30  	IGBPIsConnected bool
    31  	State           SurfaceState
    32  	HasRequested    []bool
    33  	CurrentSlot     uint32
    34  
    35  	GpuBuffer            *gpu.Buffer
    36  	GpuBufferMemory      []byte
    37  	GpuBufferMemoryAlloc uintptr
    38  	GraphicBuffers       []GraphicBuffer
    39  	CurrentFence         Fence
    40  	GRBuff               *nv.GraphicBuffer
    41  
    42  	status int
    43  }
    44  
    45  func (s *Surface) Connect() (int, error) {
    46  	if s.IGBPIsConnected {
    47  		return s.status, nil
    48  	}
    49  
    50  	status, _, err := IGBPConnect(s.IGBP, NativeWindowAPICPU, false)
    51  	s.status = status
    52  	if err != nil {
    53  		return status, err
    54  	}
    55  	s.IGBPIsConnected = true
    56  
    57  	return status, err
    58  }
    59  
    60  func (s *Surface) Disconnect() {
    61  	if s.IGBPIsConnected {
    62  		_, _ = IGBPDisconnect(s.IGBP, 2, DisconnectAllLocal)
    63  	}
    64  	s.IGBPIsConnected = false
    65  }
    66  
    67  func (s *Surface) Destroy() {
    68  	if s.State == SURFACE_STATE_INVALID {
    69  		return
    70  	}
    71  
    72  	_, _ = s.DequeueBuffer()
    73  
    74  	s.Disconnect()
    75  	_ = vi.AdjustRefCount(s.IGBP.IgbpBinder.Handle, -1, 1)
    76  	_ = vi.CloseLayer(s.LayerId)
    77  
    78  	aruid, err := am.IwcGetAppletResourceUserId()
    79  	if err != nil {
    80  		return
    81  	}
    82  	if aruid == 0 {
    83  		_ = vi.DestroyManagedLayer(s.LayerId)
    84  	}
    85  	s.State = SURFACE_STATE_INVALID
    86  
    87  	svc.SetMemoryAttribute(uintptr(unsafe.Pointer(&s.GpuBufferMemory[0])), uintptr(0x3c0000*len(s.GraphicBuffers)), 0, 0)
    88  	_, _, _ = s.GpuBuffer.Destroy()
    89  	s.GpuBufferMemory = nil
    90  }
    91  
    92  func (s *Surface) CloseLayer() error {
    93  	if displayInitializations < 1 {
    94  		return nxerrors.DisplayNotInitialized
    95  	}
    96  
    97  	s.Destroy()
    98  	_ = vi.AdjustRefCount(s.IGBP.IgbpBinder.Handle, -1, 1)
    99  	_ = vi.CloseLayer(s.LayerId)
   100  	_ = vi.DestroyManagedLayer(s.LayerId)
   101  
   102  	return nil
   103  }
   104  
   105  func (s *Surface) DequeueBuffer() ([]byte, error) {
   106  	if s.State != SURFACE_STATE_QUEUED {
   107  		return nil, nxerrors.SurfaceInvalidState
   108  	}
   109  
   110  	bf := s.GraphicBuffers[s.CurrentSlot]
   111  
   112  	status, slot, fence, _, err := IGBPDequeueBuffer(s.IGBP, bf.Width, bf.Height, bf.Format, bf.Usage, false)
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  
   117  	s.CurrentSlot = slot
   118  	s.CurrentFence = fence
   119  
   120  	if status != 0 {
   121  		return nil, nxerrors.SurfaceBufferDequeueFailed
   122  	}
   123  	if !s.HasRequested[s.CurrentSlot] {
   124  		_, _, err = IGBPRequestBuffer(s.IGBP, s.CurrentSlot)
   125  		if err != nil {
   126  			return nil, err
   127  		}
   128  		s.HasRequested[s.CurrentSlot] = true
   129  	}
   130  
   131  	imageSlice := s.GpuBufferMemory[(s.CurrentSlot * s.GRBuff.TotalSize):]
   132  	s.State = SURFACE_STATE_DEQUEUED
   133  
   134  	return imageSlice, nil
   135  }
   136  
   137  func (s *Surface) QueueBuffer() error {
   138  	if s.State != SURFACE_STATE_DEQUEUED {
   139  		return nxerrors.SurfaceInvalidState
   140  	}
   141  
   142  	qbi := &QueueBufferInput{
   143  		Size:    uint32(unsafe.Sizeof(QueueBufferInput{}) - 8),
   144  		Unknown: [2]uint32{0, 1},
   145  		Fence: Fence{
   146  			IsValid: 1,
   147  			Sync: [4]gpu.Fence{
   148  				{SyncptId: 0xffffffff},
   149  				{SyncptId: 0xffffffff},
   150  				{SyncptId: 0xffffffff},
   151  				{SyncptId: 0xffffffff},
   152  			},
   153  		},
   154  	}
   155  
   156  	_, status, err := IGBPQueueBuffer(s.IGBP, int(s.CurrentSlot), qbi)
   157  	if err != nil {
   158  		return err
   159  	}
   160  
   161  	if status != 0 {
   162  		return nxerrors.SurfaceBufferQueueFailed
   163  	}
   164  
   165  	s.State = SURFACE_STATE_QUEUED
   166  
   167  	return nil
   168  }
   169  
   170  func SurfaceCreate(layerId uint64, igbp vi.IGBP) (surface *Surface, status int, err error) {
   171  	if debugDisplay {
   172  		fmt.Printf("Display::SurfaceCreate(%d, %d)\n", layerId, igbp.IgbpBinder.Handle)
   173  	}
   174  
   175  	defer func() {
   176  		if err != nil && surface != nil {
   177  			surface.Disconnect()
   178  		}
   179  	}()
   180  
   181  	format := graphics.PixelFormatRgba8888
   182  	width := uint32(1280)
   183  	height := uint32(720)
   184  	numFbs := 2
   185  
   186  	nvColorFmt := nv.ColorFormatTable[format-graphics.PixelFormatRgba8888]
   187  	bytesPerPixel := uint32(nvColorFmt>>3) & 0x1f
   188  	blockHeightLog2 := uint32(4) // According to TRM this is the optimal value (SIXTEEN_GOBS)
   189  	blockHeight := uint32(8 * (1 << blockHeightLog2))
   190  
   191  	grBuf := nv.GraphicBuffer{}
   192  	grBuf.Header.NumInts = int32(uint64(unsafe.Sizeof(grBuf)-unsafe.Sizeof(nxtypes.NativeHandle{})) / 4)
   193  	grBuf.Unk0 = -1
   194  	grBuf.Magic = 0xDAFFCAFF
   195  	grBuf.PID = 42
   196  	grBuf.Usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE
   197  	grBuf.Format = uint32(format)
   198  	grBuf.ExtFormat = grBuf.Format
   199  	grBuf.NumPlanes = 1
   200  
   201  	grBuf.Planes[0].Width = width
   202  	grBuf.Planes[0].Height = height
   203  	grBuf.Planes[0].ColorFormat = nvColorFmt
   204  	grBuf.Planes[0].Layout = nv.LayoutBlockLinear
   205  	grBuf.Planes[0].Kind = nv.KindPitch
   206  	grBuf.Planes[0].BlockHeightLog2 = blockHeightLog2
   207  
   208  	widthAlignedBytes := (width*bytesPerPixel + 63) & ^uint32(63) // GOBs are 64 bytes wide
   209  	widthAligned := widthAlignedBytes / bytesPerPixel
   210  	heightAligned := (height + blockHeight - 1) & ^(blockHeight - 1)
   211  	fbSize := widthAlignedBytes * heightAligned
   212  	bufferSize := int(((uint32(numFbs) * fbSize) + 0xFFF) & ^uint32(0xFFF))
   213  
   214  	surface = &Surface{
   215  		LayerId:        layerId,
   216  		IGBP:           igbp,
   217  		State:          SURFACE_STATE_INVALID,
   218  		CurrentSlot:    0,
   219  		GraphicBuffers: []GraphicBuffer{},
   220  		GRBuff:         &grBuf,
   221  	}
   222  
   223  	surface.GpuBufferMemory = memory.AllocPages(bufferSize, bufferSize)
   224  	if surface.GpuBufferMemory == nil {
   225  		return nil, status, nxerrors.OutOfMemory
   226  	}
   227  
   228  	if debugDisplay {
   229  		fmt.Printf("Allocated %d bytes\n", len(surface.GpuBufferMemory))
   230  	}
   231  
   232  	surface.GpuBuffer, err = gpu.CreateBuffer(unsafe.Pointer(&surface.GpuBufferMemory[0]), uintptr(bufferSize), 0, 0x1000, grBuf.Planes[0].Kind)
   233  	if err != nil {
   234  		return nil, status, err
   235  	}
   236  
   237  	nvmapId, err := surface.GpuBuffer.GetID()
   238  	if err != nil {
   239  		_, _, _ = surface.GpuBuffer.Destroy()
   240  		return nil, status, err
   241  	}
   242  
   243  	grBuf.NVMapID = int32(nvmapId)
   244  	grBuf.Stride = widthAligned
   245  	grBuf.TotalSize = fbSize
   246  	grBuf.Planes[0].Pitch = widthAlignedBytes
   247  	grBuf.Planes[0].Size = uint64(fbSize)
   248  
   249  	for i := 0; i < numFbs; i++ {
   250  		grBuf.Planes[0].Offset = uint32(i) * fbSize
   251  		surface.GraphicBuffers = append(surface.GraphicBuffers, GraphicBuffer{
   252  			GRBuff:    &grBuf,
   253  			Width:     grBuf.Planes[0].Width,
   254  			Height:    grBuf.Planes[0].Height,
   255  			Stride:    grBuf.Stride,
   256  			Format:    format,
   257  			Length:    fbSize,
   258  			Usage:     grBuf.Usage,
   259  			GPUBuffer: surface.GpuBuffer,
   260  		})
   261  		surface.HasRequested = append(surface.HasRequested, false)
   262  		if debugDisplay {
   263  			fmt.Printf("Pre-allocating buffer %d\n", i)
   264  		}
   265  		err = IGBPSetPreallocatedBuffer(surface.IGBP, i, &surface.GraphicBuffers[i])
   266  		if err != nil {
   267  			return nil, 0, err
   268  		}
   269  	}
   270  
   271  	surface.State = SURFACE_STATE_QUEUED
   272  	return surface, status, nil
   273  }
   274  
   275  func (s *Surface) refreshFrame(f *Frame) error {
   276  	data, err := s.DequeueBuffer()
   277  	if err != nil {
   278  		return err
   279  	}
   280  	slot := s.CurrentSlot
   281  
   282  	f.surfaceBuff = data
   283  	f.bounds = image.Rect(0, 0, int(s.GraphicBuffers[slot].Width), int(s.GraphicBuffers[slot].Height))
   284  
   285  	b := f.bounds.Size()
   286  	l := b.X * b.Y * 4 // uint32 per pixel
   287  
   288  	if len(f.buff) != l {
   289  		if debugDisplay {
   290  			fmt.Printf("Allocating buffer of %d bytes\n", l)
   291  		}
   292  		f.buff = make([]byte, l)
   293  	}
   294  
   295  	return nil
   296  }
   297  
   298  // GetFrame returns a frame to be draw on screen
   299  func (s *Surface) GetFrame() (*Frame, error) {
   300  	f := &Frame{
   301  		surface:     s,
   302  		buff:        nil,
   303  		surfaceBuff: nil,
   304  	}
   305  
   306  	err := s.refreshFrame(f)
   307  	if err != nil {
   308  		return nil, err
   309  	}
   310  
   311  	return f, nil
   312  }