Project

General

Profile

Download (13.8 KB) Statistics
| Branch: | Revision:

library / src / main / java / org / distorted / library / main / EffectQueue.java @ d5b709df

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2016 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Distorted.                                                               //
5
//                                                                                               //
6
// Distorted is free software: you can redistribute it and/or modify                             //
7
// it under the terms of the GNU General Public License as published by                          //
8
// the Free Software Foundation, either version 2 of the License, or                             //
9
// (at your option) any later version.                                                           //
10
//                                                                                               //
11
// Distorted is distributed in the hope that it will be useful,                                  //
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
14
// GNU General Public License for more details.                                                  //
15
//                                                                                               //
16
// You should have received a copy of the GNU General Public License                             //
17
// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
18
///////////////////////////////////////////////////////////////////////////////////////////////////
19

    
20
package org.distorted.library.main;
21

    
22
import org.distorted.library.effect.Effect;
23
import org.distorted.library.effect.EffectName;
24
import org.distorted.library.effect.EffectType;
25
import org.distorted.library.message.EffectListener;
26
import org.distorted.library.message.EffectMessage;
27
import org.distorted.library.message.EffectMessageSender;
28

    
29
import java.util.ArrayList;
30
import java.util.HashMap;
31

    
32
///////////////////////////////////////////////////////////////////////////////////////////////////
33

    
34
abstract class EffectQueue implements DistortedMaster.Slave
35
  {
36
  private static final int CREATE = 0;
37
  private static final int ATTACH = 1;
38
  private static final int DETACH = 2;
39
  private static final int DETALL = 3;
40

    
41
  int mNumEffects;              // 'ToBe' will be more than mNumEffects if doWork() hasn't
42
  private int mNumEffectsToBe;  // added them yet (or less if it hasn't removed some yet)
43
  float[] mUniforms;
44
  long[] mCurrentDuration;
45
  Effect[] mEffects;
46
  int[] mName;
47
  long mTime=0;
48
  ArrayList<EffectListener> mListeners =null;
49
  int mNumListeners=0;  // ==mListeners.length(), but we only create mListeners if the first one gets added
50
  long mDistortedEffectsID;
51

    
52
  private static int[] mMax = new int[EffectType.LENGTH];
53
  private static long mNextID;
54
  private static HashMap<ArrayList<Long>,Long> mMapID = new HashMap<>(); // maps lists of Effect IDs (longs) to a
55
                                                                         // single long - the queue ID.
56
  private long mID;
57
  private int mIndex;
58
  private boolean mCreated;
59

    
60
  private class Job
61
    {
62
    int type;
63
    int num;
64
    boolean notify;
65
    Effect effect;
66

    
67
    Job(int t, int m, boolean n, Effect e)
68
      {
69
      type  = t;
70
      num   = m;
71
      notify= n;
72
      effect= e;
73
      }
74
    }
75

    
76
  private ArrayList<Job> mJobs = new ArrayList<>();
77

    
78
  static
79
    {
80
    onDestroy();
81
    }
82
  
83
///////////////////////////////////////////////////////////////////////////////////////////////////
84
   
85
  EffectQueue(long id, int numUniforms, int index)
86
    {
87
    mCreated            = false;
88
    mID                 = 0;
89
    mNumEffects         = 0;
90
    mNumEffectsToBe     = 0;
91
    mDistortedEffectsID = id;
92
    mIndex              = index;
93

    
94
    mJobs.add(new Job(CREATE,numUniforms,false,null));  // create the stuff that depends on max number
95
    DistortedMaster.newSlave(this);                     // of uniforms later, on first render.
96
    }
97

    
98
///////////////////////////////////////////////////////////////////////////////////////////////////
99

    
100
  static void allocateQueues(EffectQueue[] queues, EffectQueue[] from, int flags, long id)
101
    {
102
    queues[0] = (flags & Distorted.CLONE_MATRIX     ) != 0 ? from[0] : new EffectQueueMatrix(id);
103
    queues[1] = (flags & Distorted.CLONE_VERTEX     ) != 0 ? from[1] : new EffectQueueVertex(id);
104
    queues[2] = (flags & Distorted.CLONE_FRAGMENT   ) != 0 ? from[2] : new EffectQueueFragment(id);
105
    queues[3] = (flags & Distorted.CLONE_POSTPROCESS) != 0 ? from[3] : new EffectQueuePostprocess(id);
106
    }
107

    
108
///////////////////////////////////////////////////////////////////////////////////////////////////
109

    
110
  static void compute(EffectQueue[] queues, long currTime, float halfW, float halfH, float halfZ )
111
    {
112
    ((EffectQueueMatrix     )queues[0]).compute(currTime);
113
    ((EffectQueueVertex     )queues[1]).compute(currTime,halfW,halfH,halfZ);
114
    ((EffectQueueFragment   )queues[2]).compute(currTime,halfW,halfH,halfZ);
115
    ((EffectQueuePostprocess)queues[3]).compute(currTime);
116
    }
117

    
118
///////////////////////////////////////////////////////////////////////////////////////////////////
119

    
120
  static void send(EffectQueue[] queues, DistortedOutputSurface surface, float inflate, float halfW, float halfH, float halfZ, int variant )
121
    {
122
    ((EffectQueueMatrix  )queues[0]).send(surface,halfW,halfH,halfZ, variant);
123
    ((EffectQueueVertex  )queues[1]).send(inflate, variant);
124
    ((EffectQueueFragment)queues[2]).send(variant);
125
    }
126

    
127
///////////////////////////////////////////////////////////////////////////////////////////////////
128

    
129
  static float[] getMVP(EffectQueue[] queues)
130
    {
131
    return ((EffectQueueMatrix)queues[0]).getMVP();
132
    }
133

    
134
///////////////////////////////////////////////////////////////////////////////////////////////////
135

    
136
  static void getUniforms(int programH, int variant)
137
    {
138
    EffectQueueFragment.getUniforms(programH,variant);
139
    EffectQueueVertex  .getUniforms(programH,variant);
140
    EffectQueueMatrix  .getUniforms(programH,variant);
141
    }
142

    
143
///////////////////////////////////////////////////////////////////////////////////////////////////
144
// Every effect queue has an ID, which should be the same iff two queues hold the same effects.
145
// (this is a speedup: then both queues can be applied once, which seriously speeds up stuff -
146
// especially important in case of postprocessing)
147

    
148
  private void regenerateID()
149
    {
150
    if( mNumEffects>0 )
151
      {
152
      ArrayList<Long> list = new ArrayList<>();
153
      for (int i = 0; i < mNumEffects; i++) list.add(mEffects[i].getID());
154
      Long id = mMapID.get(list);
155

    
156
      if( id!=null )
157
        {
158
        mID = id;
159
        }
160
      else
161
        {
162
        mMapID.put(list,mNextID);
163
        mID = mNextID++;
164
        }
165
      }
166
    else
167
      {
168
      mID = 0;
169
      }
170
    }
171

    
172
///////////////////////////////////////////////////////////////////////////////////////////////////
173

    
174
  @SuppressWarnings("unused")
175
  int getNumEffects()
176
    {
177
    return mNumEffects;  
178
    }
179

    
180
///////////////////////////////////////////////////////////////////////////////////////////////////
181

    
182
  long getID()
183
    {
184
    return mID;
185
    }
186

    
187
///////////////////////////////////////////////////////////////////////////////////////////////////
188

    
189
  static boolean setMax(int index, int m)
190
    {
191
    if( !Distorted.isInitialized() || m<=mMax[index] )
192
      {
193
      mMax[index] = m<0 ? 0:m;
194
      return true;
195
      }
196

    
197
    return false;
198
    }
199

    
200
///////////////////////////////////////////////////////////////////////////////////////////////////
201

    
202
  static int getMax(int index)
203
    {
204
    return mMax[index];
205
    }
206

    
207
///////////////////////////////////////////////////////////////////////////////////////////////////
208

    
209
  void registerForMessages(EffectListener el)
210
    {
211
    if( mListeners==null ) mListeners = new ArrayList<>();
212

    
213
    if( !mListeners.contains(el) )
214
      {
215
      mListeners.add(el);
216
      mNumListeners++;
217
      }
218
    }
219
 
220
///////////////////////////////////////////////////////////////////////////////////////////////////
221

    
222
  void deregisterForMessages(EffectListener el)
223
    {
224
    if( mListeners.remove(el) )
225
      {
226
      mNumListeners--;
227
      }
228
    }
229

    
230
///////////////////////////////////////////////////////////////////////////////////////////////////
231

    
232
  static void onDestroy()
233
    {
234
    mNextID = 1;
235
    mMapID.clear();
236
    EffectType.reset(mMax);
237
    }
238

    
239
///////////////////////////////////////////////////////////////////////////////////////////////////
240
// this assumes 0<=effect<mNumEffects
241

    
242
  protected void remove(int effect)
243
    {
244
    mNumEffects--;
245

    
246
    long removedID = mEffects[effect].getID();
247

    
248
    for(int j=effect; j<mNumEffects; j++ )
249
      {
250
      mEffects[j]         = mEffects[j+1];
251
      mCurrentDuration[j] = mCurrentDuration[j+1];
252
      mName[j]            = mName[j+1];
253
      }
254

    
255
    mEffects[mNumEffects] = null;
256

    
257
    for(int i=0; i<mNumListeners; i++)
258
      EffectMessageSender.newMessage( mListeners.get(i), EffectMessage.EFFECT_REMOVED, removedID, mDistortedEffectsID);
259
    }
260

    
261
///////////////////////////////////////////////////////////////////////////////////////////////////
262

    
263
  synchronized int removeByName(EffectName name)
264
    {
265
    int ret = 0;
266

    
267
    for(int i=0; i<mNumEffects; i++)
268
      {
269
      if( mEffects[i].getName() == name )
270
        {
271
        mJobs.add(new Job(DETACH,0,true,mEffects[i]));
272
        ret++;
273
        }
274
      }
275

    
276
    if( ret>0 )
277
      {
278
      DistortedMaster.newSlave(this);
279
      mNumEffectsToBe-=ret;
280
      }
281

    
282
    return ret;
283
    }
284

    
285
///////////////////////////////////////////////////////////////////////////////////////////////////
286

    
287
  synchronized int removeById(long id)
288
    {
289
    for(int i=0; i<mNumEffects; i++)
290
      {
291
      if( mEffects[i].getID() == id )
292
        {
293
        mJobs.add(new Job(DETACH,0,true,mEffects[i]));
294
        DistortedMaster.newSlave(this);
295
        mNumEffectsToBe--;
296
        return 1;
297
        }
298
      }
299

    
300
    return 0;
301
    }
302

    
303
///////////////////////////////////////////////////////////////////////////////////////////////////
304

    
305
  synchronized int removeEffect(Effect effect)
306
    {
307
    for(int i=0; i<mNumEffects; i++)
308
      {
309
      if( mEffects[i]==effect )
310
        {
311
        mJobs.add(new Job(DETACH,0,true,mEffects[i]));
312
        DistortedMaster.newSlave(this);
313
        mNumEffectsToBe--;
314
        return 1;
315
        }
316
      }
317
   
318
    return 0;
319
    }
320

    
321
///////////////////////////////////////////////////////////////////////////////////////////////////
322
// we do want to notify Listeners if they called 'abortAll' themselves but don't want to notify
323
// them if it is the library itself which is releasing resources.
324

    
325
  synchronized int abortAll(boolean notify)
326
    {
327
    mJobs.add(new Job(DETALL,0,notify,null));
328
    DistortedMaster.newSlave(this);
329
    mNumEffectsToBe = 0;
330
    return mNumEffects;
331
    }
332

    
333
///////////////////////////////////////////////////////////////////////////////////////////////////
334
  
335
  boolean add(Effect effect)
336
    {
337
    if( mMax[mIndex]>mNumEffectsToBe || !mCreated )
338
      {
339
      mJobs.add(new Job(ATTACH,0,false,effect));
340
      DistortedMaster.newSlave(this);
341
      mNumEffectsToBe++;
342
      return true;
343
      }
344

    
345
    return false;
346
    }
347

    
348
///////////////////////////////////////////////////////////////////////////////////////////////////
349
/**
350
 * This is not really part of the public API. Has to be public only because it is a part of the
351
 * DistortedSlave interface, which should really be a class that we extend here instead but
352
 * Java has no multiple inheritance.
353
 *
354
 * @y.exclude
355
 */
356
  public void doWork()
357
    {
358
    boolean changed = false;
359
    int num = mJobs.size();
360
    Job job;
361

    
362
    for(int i=0; i<num; i++)
363
      {
364
      job = mJobs.remove(0);
365

    
366
      switch(job.type)
367
        {
368
        case CREATE: int max = mMax[mIndex];
369
                     if( max>0 )
370
                       {
371
                       mUniforms        = new float[max*job.num];
372
                       mCurrentDuration = new long[max];
373
                       mEffects         = new Effect[max];
374
                       mName            = new int[max];
375
                       }
376
                     mCreated = true;
377

    
378
                     break;
379
        case ATTACH: if( mMax[mIndex]>mNumEffects ) // it is possible that we have first
380
                       {                            // added effects and then lowered mMax
381
                       mCurrentDuration[mNumEffects] = 0;
382
                       mEffects[mNumEffects] = job.effect;
383
                       mName[mNumEffects] = job.effect.getName().ordinal();
384
                       mNumEffects++;
385
                       changed = true;
386
                       }
387
                     else
388
                       {
389
                       android.util.Log.e("queue", "failed to add effect "+job.effect.getName());
390
                       }
391
                     break;
392
        case DETACH: for(int j=0; j<mNumEffects; j++)
393
                       {
394
                       if (mEffects[j] == job.effect)
395
                         {
396
                         remove(j);
397
                         changed = true;
398
                         break;
399
                         }
400
                       }
401
                     break;
402
        case DETALL: for(int j=0; j<mNumEffects; j++ )
403
                       {
404
                       changed = true;
405

    
406
                       if( job.notify )
407
                         {
408
                         for(int k=0; k<mNumListeners; k++)
409
                           EffectMessageSender.newMessage( mListeners.get(k), EffectMessage.EFFECT_REMOVED, mEffects[j].getID(), mDistortedEffectsID);
410
                         }
411

    
412
                       mEffects[j] = null;
413
                       }
414

    
415
                     mNumEffects= 0;
416
                     break;
417
        }
418
      }
419

    
420
    if( changed && mIndex==EffectType.POSTPROCESS.ordinal() ) regenerateID();
421
    }
422
  }
(15-15/19)