package rr.parallel; import data.Tables; import static data.Tables.finetangent; import doom.DoomMain; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import static m.fixed_t.*; import rr.IDetailAware; import v.tables.LightsAndColors; import rr.TextureManager; import rr.drawfuns.ColVars; import rr.drawfuns.DoomColumnFunction; import rr.drawfuns.R_DrawColumnBoomOpt; import rr.drawfuns.R_DrawColumnBoomOptLow; import rr.visplane_t; /** This is what actual executes the RenderSegInstructions. * * * Each thread actually operates on a FIXED PORTION OF THE SCREEN * (e.g. half-width, third-width etc.) and only renders the portions * of the RenderSegInstructions that are completely contained * within its own screen area. For this reason, all threads * check out all RenderSegInstructions of the list, and render any * and all portions that are within their responsability domain, so * to speak. * * FIXME there's a complex data dependency with ceilingclip/floorclip * I was not quite able to fix yet. Practically, in the serial renderer, * calls to RenderSegLoop are done in a correct, non-overlapping order, * and certain parts are drawn before others in order to set current * floor/ceiling markers and visibility e.g. think of a wall visible * through windows. * * FIXME 7/6/2011 Data dependencies and per-thread clipping are now * fixed, however there is still visible "jitter" or "noise" on some * of the walls, probably related to column offsets. * * @author velktron * */ public abstract class RenderSegExecutor<T,V> implements Runnable, IDetailAware { // This needs to be set by the partitioner. protected int rw_start, rw_end,rsiend; // These need to be set on creation, and are unchangeable. protected final LightsAndColors<V> colormaps; protected final TextureManager<T> TexMan; protected final CyclicBarrier barrier; protected RenderSegInstruction<V>[] RSI; protected final long[] xtoviewangle; protected final short[] ceilingclip, floorclip; // Each thread should do its own ceiling/floor blanking protected final short[] BLANKFLOORCLIP; protected final short[] BLANKCEILINGCLIP; protected static final int HEIGHTBITS = 12; protected static final int HEIGHTUNIT = (1<<HEIGHTBITS); protected final int id; protected DoomColumnFunction<T,V> colfunchi,colfunclow; protected DoomColumnFunction<T,V> colfunc; protected ColVars<T,V> dcvars; protected final DoomMain<T, V> DOOM; public RenderSegExecutor(DoomMain<T, V> DOOM,int id,V screen, TextureManager<T> texman, RenderSegInstruction<V>[] RSI, short[] BLANKCEILINGCLIP, short[] BLANKFLOORCLIP, short[] ceilingclip, short[] floorclip, int[] columnofs, long[] xtoviewangle, int[] ylookup, visplane_t[] visplanes, CyclicBarrier barrier, LightsAndColors<V> colormaps){ this.id=id; this.TexMan=texman; this.RSI=RSI; this.barrier=barrier; this.ceilingclip=ceilingclip; this.floorclip=floorclip; this.xtoviewangle=xtoviewangle; this.BLANKCEILINGCLIP=BLANKCEILINGCLIP; this.BLANKFLOORCLIP=BLANKFLOORCLIP; this.colormaps=colormaps; this.DOOM=DOOM; } protected final void ProcessRSI(RenderSegInstruction<V> rsi, int startx,int endx,boolean contained){ int angle; // angle_t int index; int yl; // low int yh; // hight int mid; int pixlow,pixhigh,pixhighstep,pixlowstep; int rw_scale,topfrac,bottomfrac,bottomstep; // These are going to be modified A LOT, so we cache them here. pixhighstep=rsi.pixhighstep; pixlowstep=rsi.pixlowstep; bottomstep=rsi.bottomstep; // We must re-scale it. int rw_scalestep= rsi.rw_scalestep; int topstep=rsi.topstep; int texturecolumn=0; // fixed_t final int bias; // Well is entirely contained in our screen zone // (or the very least it starts in it). if (contained) bias=0; // We are continuing a wall that started in another // screen zone. else bias=(startx-rsi.rw_x); // PROBLEM: these must be pre-biased when multithreading. rw_scale=rsi.rw_scale+bias*rw_scalestep; topfrac = rsi.topfrac+bias*topstep; bottomfrac = rsi.bottomfrac+ bias*bottomstep; pixlow=rsi.pixlow+bias*pixlowstep; pixhigh=rsi.pixhigh+bias*pixhighstep; { for ( int rw_x=startx; rw_x < endx ; rw_x++) { // mark floor / ceiling areas yl = (topfrac+HEIGHTUNIT-1)>>HEIGHTBITS; // no space above wall? if (yl < ceilingclip[rw_x]+1) yl = ceilingclip[rw_x]+1; yh = bottomfrac>>HEIGHTBITS; if (yh >= floorclip[rw_x]) yh = floorclip[rw_x]-1; // System.out.printf("Thread: rw %d yl %d yh %d\n",rw_x,yl,yh); // A particular seg has been identified as a floor marker. // texturecolumn and lighting are independent of wall tiers if (rsi.segtextured) { // calculate texture offset // CAREFUL: a VERY anomalous point in the code. Their sum is supposed // to give an angle not exceeding 45 degrees (or 0x0FFF after shifting). // If added with pure unsigned rules, this doesn't hold anymore, // not even if accounting for overflow. angle = Tables.toBAMIndex(rsi.rw_centerangle + (int)xtoviewangle[rw_x]); //angle = (int) (((rw_centerangle + xtoviewangle[rw_x])&BITS31)>>>ANGLETOFINESHIFT); //angle&=0x1FFF; // FIXME: We are accessing finetangent here, the code seems pretty confident // in that angle won't exceed 4K no matter what. But xtoviewangle // alone can yield 8K when shifted. // This usually only overflows if we idclip and look at certain directions // (probably angles get fucked up), however it seems rare enough to just // "swallow" the exception. You can eliminate it by anding with 0x1FFF // if you're so inclined. texturecolumn = rsi.rw_offset-FixedMul(finetangent[angle],rsi.rw_distance); texturecolumn >>= FRACBITS; // calculate lighting index = rw_scale>>colormaps.lightScaleShift(); if (index >= colormaps.maxLightScale() ) index = colormaps.maxLightScale()-1; dcvars.dc_colormap = rsi.walllights[index]; dcvars.dc_x = rw_x; dcvars.dc_iscale = (int) (0xffffffffL / rw_scale); } // draw the wall tiers if (rsi.midtexture!=0) { // single sided line dcvars.dc_yl = yl; dcvars.dc_yh = yh; dcvars.dc_texheight = TexMan.getTextureheight(rsi.midtexture)>>FRACBITS; // killough dcvars.dc_texturemid = rsi.rw_midtexturemid; dcvars.dc_source = TexMan.GetCachedColumn(rsi.midtexture,texturecolumn); dcvars.dc_source_ofs=0; colfunc.invoke(); ceilingclip[rw_x] = (short) rsi.viewheight; floorclip[rw_x] = -1; } else { // two sided line if (rsi.toptexture!=0) { // top wall mid = pixhigh>>HEIGHTBITS; pixhigh += pixhighstep; if (mid >= floorclip[rw_x]) mid = floorclip[rw_x]-1; if (mid >= yl) { dcvars.dc_yl = yl; dcvars.dc_yh = mid; dcvars.dc_texturemid = rsi.rw_toptexturemid; dcvars.dc_texheight=TexMan.getTextureheight(rsi.toptexture)>>FRACBITS; dcvars.dc_source = TexMan.GetCachedColumn(rsi.toptexture,texturecolumn); //dc_source_ofs=0; colfunc.invoke(); ceilingclip[rw_x] = (short) mid; } else ceilingclip[rw_x] = (short) (yl-1); } // if toptexture else { // no top wall if (rsi.markceiling) ceilingclip[rw_x] = (short) (yl-1); } if (rsi.bottomtexture!=0) { // bottom wall mid = (pixlow+HEIGHTUNIT-1)>>HEIGHTBITS; pixlow += pixlowstep; // no space above wall? if (mid <= ceilingclip[rw_x]) mid = ceilingclip[rw_x]+1; if (mid <= yh) { dcvars.dc_yl = mid; dcvars.dc_yh = yh; dcvars.dc_texturemid = rsi.rw_bottomtexturemid; dcvars.dc_texheight=TexMan.getTextureheight(rsi.bottomtexture)>>FRACBITS; dcvars.dc_source = TexMan.GetCachedColumn(rsi.bottomtexture,texturecolumn); // dc_source_ofs=0; colfunc.invoke(); floorclip[rw_x] = (short) mid; } else floorclip[rw_x] = (short) (yh+1); } // end-bottomtexture else { // no bottom wall if (rsi.markfloor) floorclip[rw_x] = (short) (yh+1); } } // end-else (two-sided line) rw_scale += rw_scalestep; topfrac += topstep; bottomfrac += bottomstep; } // end-rw } // end-block } @Override public void setDetail(int detailshift) { if (detailshift == 0) colfunc = colfunchi; else colfunc = colfunclow; } /** Only called once per screen width change */ public void setScreenRange(int rwstart, int rwend){ this.rw_end=rwend; this.rw_start=rwstart; } /** How many instructions TOTAL are there to wade through. * Not all will be executed on one thread, except in some rare * circumstances. * * @param rsiend */ public void setRSIEnd(int rsiend){ this.rsiend=rsiend; } public void run() { RenderSegInstruction<V> rsi; // Each worker blanks its own portion of the floor/ceiling clippers. System.arraycopy(BLANKFLOORCLIP,rw_start,floorclip, rw_start,rw_end-rw_start); System.arraycopy(BLANKCEILINGCLIP,rw_start,ceilingclip, rw_start,rw_end-rw_start); // For each "SegDraw" instruction... for (int i=0;i<rsiend;i++){ rsi=RSI[i]; dcvars.centery=RSI[i].centery; int startx,endx; // Does a wall actually start in our screen zone? // If yes, we need no bias, since it was meant for it. // If the wall started BEFORE our zone, then we // will need to add a bias to it (see ProcessRSI). // If its entirely non-contained, ProcessRSI won't be // called anyway, so we don't need to check for the end. boolean contained=(rsi.rw_x>=rw_start); // Keep to your part of the screen. It's possible that several // threads will process the same RSI, but different parts of it. // Trim stuff that starts before our rw_start position. startx=Math.max(rsi.rw_x,rw_start); // Similarly, trim stuff after our rw_end position. endx=Math.min(rsi.rw_stopx,rw_end); // Is there anything to actually draw? if ((endx-startx)>0) { ProcessRSI(rsi,startx,endx,contained); } } // end-instruction try { barrier.await(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (BrokenBarrierException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //protected abstract void ProcessRSI(RenderSegInstruction<V> rsi, int startx,int endx,boolean contained); ////////////////////////////VIDEO SCALE STUFF //////////////////////////////// public void updateRSI(RenderSegInstruction<V>[] rsi) { this.RSI=rsi; } public static final class TrueColor extends RenderSegExecutor<byte[],int[]>{ public TrueColor(DoomMain<byte[], int[]> DOOM, int id, int[] screen, TextureManager<byte[]> texman, RenderSegInstruction<int[]>[] RSI, short[] BLANKCEILINGCLIP, short[] BLANKFLOORCLIP, short[] ceilingclip, short[] floorclip, int[] columnofs, long[] xtoviewangle, int[] ylookup, visplane_t[] visplanes, CyclicBarrier barrier, LightsAndColors<int[]> colormaps) { super(DOOM, id, screen, texman, RSI, BLANKCEILINGCLIP, BLANKFLOORCLIP, ceilingclip, floorclip, columnofs, xtoviewangle, ylookup, visplanes, barrier, colormaps); dcvars=new ColVars<>(); colfunc=colfunchi=new R_DrawColumnBoomOpt.TrueColor(DOOM.vs.getScreenWidth(), DOOM.vs.getScreenHeight(),ylookup,columnofs,dcvars,screen,null ); colfunclow=new R_DrawColumnBoomOptLow.TrueColor(DOOM.vs.getScreenWidth(), DOOM.vs.getScreenHeight(),ylookup,columnofs,dcvars,screen,null ); } } public static final class HiColor extends RenderSegExecutor<byte[],short[]>{ public HiColor(DoomMain<byte[], short[]> DOOM, int id, short[] screen, TextureManager<byte[]> texman, RenderSegInstruction<short[]>[] RSI, short[] BLANKCEILINGCLIP, short[] BLANKFLOORCLIP, short[] ceilingclip, short[] floorclip, int[] columnofs, long[] xtoviewangle, int[] ylookup, visplane_t[] visplanes, CyclicBarrier barrier, LightsAndColors<short[]> colormaps) { super(DOOM, id, screen, texman, RSI, BLANKCEILINGCLIP, BLANKFLOORCLIP, ceilingclip, floorclip, columnofs, xtoviewangle, ylookup, visplanes, barrier, colormaps); dcvars=new ColVars<>(); colfunc=colfunchi=new R_DrawColumnBoomOpt.HiColor(DOOM.vs.getScreenWidth(), DOOM.vs.getScreenHeight(),ylookup,columnofs,dcvars,screen,null ); colfunclow=new R_DrawColumnBoomOptLow.HiColor(DOOM.vs.getScreenWidth(), DOOM.vs.getScreenHeight(),ylookup,columnofs,dcvars,screen,null ); } } public static final class Indexed extends RenderSegExecutor<byte[],byte[]>{ public Indexed(DoomMain<byte[], byte[]> DOOM, int id, byte[] screen, TextureManager<byte[]> texman, RenderSegInstruction<byte[]>[] RSI, short[] BLANKCEILINGCLIP, short[] BLANKFLOORCLIP, short[] ceilingclip, short[] floorclip, int[] columnofs, long[] xtoviewangle, int[] ylookup, visplane_t[] visplanes, CyclicBarrier barrier, LightsAndColors<byte[]> colormaps) { super(DOOM, id, screen, texman, RSI, BLANKCEILINGCLIP, BLANKFLOORCLIP, ceilingclip, floorclip, columnofs, xtoviewangle, ylookup, visplanes, barrier, colormaps); dcvars=new ColVars<>(); colfunc=colfunchi=new R_DrawColumnBoomOpt.Indexed(DOOM.vs.getScreenWidth(), DOOM.vs.getScreenHeight(),ylookup,columnofs,dcvars,screen,null ); colfunclow=new R_DrawColumnBoomOptLow.Indexed(DOOM.vs.getScreenWidth(), DOOM.vs.getScreenHeight(),ylookup,columnofs,dcvars,screen,null ); } } } // $Log: RenderSegExecutor.java,v $ // Revision 1.2 2012/09/24 17:16:22 velktron // Massive merge between HiColor and HEAD. There's no difference from now on, and development continues on HEAD. // // Revision 1.1.2.2 2012/09/21 16:18:29 velktron // More progress...but no cigar, yet. // // Revision 1.1.2.1 2012/09/20 14:20:10 velktron // Parallel stuff in their own package (STILL BROKEN) // // Revision 1.12.2.4 2012/09/19 21:45:41 velktron // More extensions... // // Revision 1.12.2.3 2012/09/18 16:11:50 velktron // Started new "all in one" approach for unifying Indexed, HiColor and (future) TrueColor branches. // // Revision 1.12.2.2 2012/06/15 14:44:09 velktron // Doesn't need walllights? // // Revision 1.12.2.1 2011/11/14 00:27:11 velktron // A barely functional HiColor branch. Most stuff broken. DO NOT USE // // Revision 1.12 2011/11/01 19:04:06 velktron // Cleaned up, using IDetailAware for subsystems. // // Revision 1.11 2011/10/31 18:33:56 velktron // Much cleaner implementation using the new rendering model. Now it's quite faster, too. // // Revision 1.10 2011/07/25 11:39:10 velktron // Optimized to work without dc_source_ofs (uses only cached, solid textures) // // Revision 1.9 2011/07/12 16:29:35 velktron // Now using GetCachedColumn // // Revision 1.8 2011/07/12 16:25:02 velktron // Removed dependency on per-thread column pointers. // // Revision 1.7 2011/06/07 21:21:15 velktron // Definitively fixed jitter bug, which was due to dc_offset_contention. Now the alternative parallel renderer is just as good as the original one. // // Revision 1.6 2011/06/07 13:35:38 velktron // Definitively fixed drawing priority/zones. Now to solve the jitter :-/ // // Revision 1.5 2011/06/07 01:32:32 velktron // Parallel Renderer 2 still buggy :-( // // Revision 1.4 2011/06/07 00:50:47 velktron // Alternate Parallel Renderer fixed. // // Revision 1.3 2011/06/07 00:11:11 velktron // Fixed alternate parallel renderer (seg based). No longer deprecated. //