Okaypeg

Image compressor for VRChat

It's a JPEG-Like format (Uses DCT, yuv space, and quantization), but differs in the metadata, and entropy compression algorithm. The entropy compressor was designed to run is as few instructions as possible, it is a fixed code table with 4 operations. It still manages to get some OKAY ratios. Image quality is fixed and is NOT GREAT, but OKAY.

Work is interleaved over frames and has a 'budget time' set in the inspector. This is by default 2 seconds. You can crank it down to as low as possible but you will notice the frames dissapearing.

NOTE: You should use this sparingly. (1-5 data stores per world)

NOTE: If you use the delta compression option, which saves some memory and speed, you only get one upload channel per person. It's not a hard requirement, but the compression ratios will become unpredictable.

Download: okaypeg0.1.unitypackage
Contains demo scene of how to use it

Example usage code:

0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

/*
 * Demo program of okaypeg to sync the image of a camera.
 */

public class okaypeg_demo: UdonSharpBehaviour
{

/* Reference to the compressor */
public okaypeg_main _compressor;

/* 
 * Two store items (main holds the large chunks of data, delta holds a smaller
 * set of blocks when possible to try and save networking usage
 */
public okaypeg_store _store_main, _store_delta;

/*
 * RT src is the texture we're reading from, so the camera's view in this demo.
 * RT dst is the destination texture we read the compressed image into.
 */
public RenderTexture _rt_src, _rt_dst;

/*
 * Signal that there is new data in _rt_src
 */
bool _picture_taken = false;

/* 
 * Take picture button
 */
void OnPickupUseDown()
{
   this.GetComponent<Camera>().Render();

   /* 
    * If we don't own it we use the claim function to take the data and reset
    * it 
    */
   if( Networking.GetOwner( _store_main.gameObject ) != Networking.LocalPlayer )
      _compressor._opg_claim_and_clear( _store_main, _store_delta );

   /* Signal to the update loop that we should compress a new image */
   _picture_taken = true;
}

float _timer = 0.0f;
const float k_timer_interval = 0.1f;
void Update()
{
   /* Use a simple timer to reduce pressure on Udon */
   _timer -= Time.deltaTime;
   if( _timer < 0.0f )
   {
      _timer = k_timer_interval;

      /* 
       * Try compress and upload the texture if we're the owner
       */
      if( Networking.GetOwner( _store_main.gameObject ) == 
            Networking.LocalPlayer )
      {
         if( _picture_taken )
         {
            if( _compressor._opg_upload( _rt_src, 512, 256, 
                                         _store_main, _store_delta ) )
            {
               _picture_taken = false;
            }
         }
      }
      
      /* 
       * Read back the compressed texture. We don't actually have to do this
       * at all if we own the object. But in the demo we use it to preview the
       * result.
       */
      _compressor._opg_decode( _store_main, _store_delta, Color.white, _rt_dst);
   }
}
}