| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757 |
- /******************************************************************************/
- #include "stdafx.h"
- namespace EE{
- /******************************************************************************
- RT : Width , Height , Type , Samples , Comments
- _main : D. resW(), D. resH(), IMAGERT_RGBA, 1 , COLOR RGB, Opacity
- _main_ds : D. resW(), D. resH(), IMAGERT_RGBA, 1 , this is the Main DepthStencil buffer to be used together with '_main' RT, on OpenGL (except iOS) it is provided by the system
- _ds : D.renderW(), D.renderW(), IMAGERT_DS , D.samples()
- _ds_1s : D.renderW(), D.renderW(), IMAGERT_DS , 1 , if '_ds' is Multi-Sampled then this is created as a standalone 1-sampled depth buffer, otherwise it's a duplicate of '_ds'
- _col : D.renderW(), D.renderH(), D.highPrecColRT() ? IMAGERT_RGBA_P : IMAGERT_RGBA, D.samples(), COLOR RGB, GLOW
- _nrm : D.renderW(), D.renderH(), D.signedNrmRT() ? .. : D.highPrecNrmRT() ? IMAGERT_RGBA_P : IMAGERT_RGBA, D.samples(), NRM XYZ, SPEC
- _vel : D.renderW(), D.renderH(), D.signedVelRT() ? IMAGERT_RGB_S : IMAGERT_RGB , D.samples(), VEL XYZ
- _pos : D.renderW(), D.renderH(), IMAGERT_F32 , 1 , Linearized depth
- _lum : D.renderW(), D.renderH(), D.highPrecLumRT() ? IMAGERT_RGBA_H : IMAGERT_RGBA, D.samples(), LIGHT RGB, LIGHT SPEC
- _lum_1s : D.renderW(), D.renderH(), D.highPrecLumRT() ? IMAGERT_RGBA_H : IMAGERT_RGBA, 1 , LIGHT RGB, LIGHT SPEC. if '_lum' is Multi-Sampled then this is created as a standalone 1-sampled depth buffer, otherwise it's a duplicate of '_lum'
- _water_col: D.renderW(), D.renderH(), IMAGERT_RGBA, 1 , COLOR RGB, UNUSED
- _water_nrm: D.renderW(), D.renderH(), D.signedNrmRT() ? IMAGERT_RGBA_S : IMAGERT_RGBA , 1 , NRM XYZ, SPEC. High precision is not needed because we just use better UnpackNormal in the shader
- _water_ds : D.renderW(), D.renderH(), IMAGERT_DS , 1 , Water Depth
- _water_lum: D.renderW(), D.renderH(), IMAGERT_RGBA, 1 , LIGHT RGB, LIGHT SPEC
- '_gui' is set to '_main', unless stereoscopic rendering is enabled then it's set to VR RT
- If '_ds' is multi-sampled on DX10+ then:
- In Deferred Renderer:
- -'_ds_1s' is set to down-sampled copy of '_ds'
- -both '_ds' and '_ds_1s' have STENCIL_REF_MSAA set
- In Non-Deferred Renderer:
- -if 'slowCombine' then '_ds_1s' has STENCIL_REF_MSAA set
- -if "Fog.draw || Sky.isActual" then '_ds' has STENCIL_REF_MSAA set
- In OpenGL (except iOS):
- '_main' and '_main_ds' don't have _rb and _txtr set, because they're provided by the system and not created by the engine.
- This means that when setting '_main' it's always paired with '_main_ds' depth buffer, and '_main_ds' can't be read as a depth texture.
- In OpenGL:
- '_main' and '_main_ds' are flipped vertically when compared to other render targets.
- /******************************************************************************/
- void RendererClass::createShadowMap()
- {
- SyncLocker locker(D._lock);
- // shadow maps
- D._shd_map_size_actual=Max(0, Min(D.shadowMapSize()*3, D.maxTexSize())/3);
- Int shd_map_w=D.shadowMapSizeActual()*2,
- shd_map_h=D.shadowMapSizeActual()*3;
- #if DX9
- _shd_map_null.del();
- if(!_shd_map.createTryEx(shd_map_w, shd_map_h, 1, IMAGE_D32 , IMAGE_SHADOW_MAP, 1)) // on GeForce 650m this is not available, but try anyway
- if(!_shd_map.createTryEx(shd_map_w, shd_map_h, 1, IMAGE_D24X8, IMAGE_SHADOW_MAP, 1)) // we don't need stencil so avoid it in case it causes performance penalty
- _shd_map.createTryEx(shd_map_w, shd_map_h, 1, IMAGE_D24S8, IMAGE_SHADOW_MAP, 1);
- if( _shd_map.is())
- if(!_shd_map_null.createTryEx(_shd_map.w(), _shd_map.h(), 1, IMAGE_NULL, IMAGE_RT, 1, 1, IMAGE_B8G8R8A8))_shd_map.del();
- #else
- if(!_shd_map.createTryEx(shd_map_w, shd_map_h, 1, IMAGE_D32 , IMAGE_SHADOW_MAP, 1)) // D32 shadow maps have no performance penalty (tested on GeForce 650m) so use them if possible
- if(!_shd_map.createTryEx(shd_map_w, shd_map_h, 1, IMAGE_D24X8, IMAGE_SHADOW_MAP, 1)) // we don't need stencil so avoid it in case it causes performance penalty
- if(!_shd_map.createTryEx(shd_map_w, shd_map_h, 1, IMAGE_D24S8, IMAGE_SHADOW_MAP, 1))
- if(!_shd_map.createTryEx(shd_map_w, shd_map_h, 1, IMAGE_D16 , IMAGE_SHADOW_MAP, 1)){}
- #endif
- if(!_shd_map.is())D._shd_map_size_actual=0;
- // cloud shadow maps
- #if DX9
- if(!_cld_map.createTryEx(D.cloudsMapSize()*2, D.cloudsMapSize()*3, 1, IMAGE_A8 , IMAGE_RT, 1, 1, IMAGE_L8A8 ))
- _cld_map.createTryEx(D.cloudsMapSize()*2, D.cloudsMapSize()*3, 1, IMAGE_B8G8R8A8, IMAGE_RT, 1, 1, IMAGE_R8G8B8A8);
- #else
- if(!_cld_map.createTryEx(D.cloudsMapSize()*2, D.cloudsMapSize()*3, 1, IMAGE_R8 , IMAGE_RT, 1, 1, IMAGE_R8G8))
- _cld_map.createTry (D.cloudsMapSize()*2, D.cloudsMapSize()*3, 1, IMAGE_R8G8B8A8, IMAGE_RT, 1);
- #endif
- Sh.connectRT();
- D.shadowJitterSet();
- }
- void RendererClass::rtClear()
- {
- _h0 .clear();
- _h1 .clear();
- _q0 .clear();
- _q1 .clear();
- _col .clear();
- _nrm .clear();
- _vel .clear();
- _lum .clear();
- _lum_1s .clear();
- _shd_1s .clear();
- _shd_ms .clear();
- _ds .clear();
- _ds_1s .clear();
- _water_col .clear();
- _water_nrm .clear();
- _water_ds .clear();
- _water_lum .clear();
- _vol .clear();
- _ao .clear();
- _mirror_rt .clear();
- _outline_rt .clear();
- _sky_coverage.clear();
- _final .clear();
- // don't clear '_back' and '_back_ds' here in case they are used
- }
- void RendererClass::rtClean()
- {
- SyncLocker locker(D._lock);
- rtClear();
- REPA(_rts)if(_rts[i].available())_rts.removeValid(i);
- }
- void RendererClass::rtDel()
- {
- SyncLocker locker(D._lock);
- #if GL
- if(FBO) // detach all render targets
- {
- D.fbo(FBO); // set custom frame buffer
- glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 , GL_TEXTURE_2D , 0, 0);
- glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1 , GL_TEXTURE_2D , 0, 0);
- glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2 , GL_TEXTURE_2D , 0, 0);
- glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3 , GL_TEXTURE_2D , 0, 0);
- glFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT , GL_TEXTURE_2D , 0, 0);
- glFramebufferTexture2D (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D , 0, 0);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT , GL_RENDERBUFFER, 0 );
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0 );
- #if !IOS
- //D.fbo(0); // set default frame buffer (iOS doesn't have it) !! Don't do this, because we clear all attachments and '_cur' to null, we assume that no RT's are set, however that's possible only in a custom FBO, so we need to keep it, so that the next 'set' RT change will be able to properly detect the change !!
- #endif
- }
- #endif
- rtClear();
- D.clearFade(); // _fade.clear(); this is already cleared in 'clearFade'
- _back .clear();
- _back_ds.clear();
- _gui =_cur_main =&_main;
- _gui_ds=_cur_main_ds=&_main_ds;
- unmapMain();
- #if DX9 || DX11 || IOS // only on these platforms we're creating custom '_main_ds', on other platforms the system creates it, so we're not deleting (to keep the info about IMAGE_TYPE and samples)
- _main_ds.del();
- #endif
- _cld_map .del();
- _shd_map .del();
- _shd_map_null.del();
- REPAO(_eye_adapt_scale).del();
- _rts.clear();
- REPAO(_cur )=null; _cur_ds =null;
- REPAO(_cur_id )=NULL; _cur_ds_id=NULL;
- REPAO(_cur_ds_ids)=NULL;
- }
- Bool RendererClass::rtCreate()
- {
- if(LogInit)LogN("RendererClass.rtCreate");
- SyncLocker locker(D._lock);
- rtDel();
- ResetImageTypeCreateResult();
- if(!D.canDraw())return true; // don't bother with render targets if the device can't draw (can happen when using 'APP_ALLOW_NO_GPU')
- if(!mapMain())return false;
- // depth
- #if DX9
- if(!_main_ds.createTryEx(_main.w(), _main.h(), 1, IMAGE_INTZ , IMAGE_DS_RT, 1, _main.samples()))
- //if(!_main_ds.createTryEx(_main.w(), _main.h(), 1, IMAGE_RAWZ , IMAGE_DS_RT, 1, _main.samples())) disable for now because all shader depth reads use standard depth format while RAWZ requires a special shader, enabling would require converting to IMAGE_F32 that mimics standard depth buffer, however that would destroy stencil and we would have to use 2 separate Images
- if(!_main_ds.createTryEx(_main.w(), _main.h(), 1, IMAGE_DF24 , IMAGE_DS_RT, 1, _main.samples()))
- if(!_main_ds.createTryEx(_main.w(), _main.h(), 1, IMAGE_D24S8, IMAGE_DS , 1, _main.samples()))
- if(!_main_ds.createTryEx(_main.w(), _main.h(), 1, IMAGE_D24X8, IMAGE_DS , 1, _main.samples()))return false;
- #elif DX11
- if(!_main_ds.createTryEx(_main.w(), _main.h(), 1, IMAGE_D24S8, IMAGE_DS_RT, 1, _main.samples()))return false;
- #elif IOS // on iOS we have access to '_main' so let's keep '_main_ds' the same
- if(!_main_ds.createTryEx(_main.w(), _main.h(), 1, IMAGE_D24S8, IMAGE_DS_RT, 1, _main.samples()))
- if(!_main_ds.createTryEx(_main.w(), _main.h(), 1, IMAGE_D32 , IMAGE_DS_RT, 1, _main.samples()))
- if(!_main_ds.createTryEx(_main.w(), _main.h(), 1, IMAGE_D24X8, IMAGE_DS_RT, 1, _main.samples()))
- if(!_main_ds.createTryEx(_main.w(), _main.h(), 1, IMAGE_D16 , IMAGE_DS_RT, 1, _main.samples()))
- if(!_main_ds.createTryEx(_main.w(), _main.h(), 1, IMAGE_D24S8, IMAGE_DS , 1, _main.samples()))
- if(!_main_ds.createTryEx(_main.w(), _main.h(), 1, IMAGE_D32 , IMAGE_DS , 1, _main.samples()))
- if(!_main_ds.createTryEx(_main.w(), _main.h(), 1, IMAGE_D24X8, IMAGE_DS , 1, _main.samples()))
- if(!_main_ds.createTryEx(_main.w(), _main.h(), 1, IMAGE_D16 , IMAGE_DS , 1, _main.samples()))return false;
- #else // other platforms have '_main_ds' linked with '_main' provided by the system
- _main_ds.forceInfo(_main.w(), _main.h(), 1, _main_ds.type() ? _main_ds.type() : IMAGE_D24S8, IMAGE_DS)._samples=_main.samples(); // if we know the type then use it, otherwise assume the default IMAGE_D24S8
- #endif
- createShadowMap();
- // eye adaptation
- _eye_adapt_scale_cur=0; if(!_eye_adapt_scale[0].createTryEx(1, 1, 1, IMAGE_F32, IMAGE_RT, 1, 1, IMAGE_F16)
- || !_eye_adapt_scale[1].createTryEx(1, 1, 1, IMAGE_F32, IMAGE_RT, 1, 1, IMAGE_F16))REPAO(_eye_adapt_scale).del();
- setMain();
- Sh.connectRT();
- return true;
- }
- /******************************************************************************/
- void RendererClass::update()
- {
- if(_t_measure)
- {
- Dbl t=Time.curTime();
- if( t>_t_last_measure+1)
- {
- Flt mul=1.0f/_t_measures[0];
- _t_reflection [0]=_t_reflection [1]*mul; _t_reflection [1]=0;
- _t_prepare [0]=_t_prepare [1]*mul; _t_prepare [1]=0;
- _t_solid [0]=_t_solid [1]*mul; _t_solid [1]=0;
- _t_overlay [0]=_t_overlay [1]*mul; _t_overlay [1]=0;
- _t_water [0]=_t_water [1]*mul; _t_water [1]=0;
- _t_light [0]=_t_light [1]*mul; _t_light [1]=0;
- _t_sky [0]=_t_sky [1]*mul; _t_sky [1]=0;
- _t_blend [0]=_t_blend [1]*mul; _t_blend [1]=0;
- _t_palette [0]=_t_palette [1]*mul; _t_palette [1]=0;
- _t_behind [0]=_t_behind [1]*mul; _t_behind [1]=0;
- _t_rays [0]=_t_rays [1]*mul; _t_rays [1]=0;
- _t_refract [0]=_t_refract [1]*mul; _t_refract [1]=0;
- _t_volumetric [0]=_t_volumetric [1]*mul; _t_volumetric [1]=0;
- _t_post_process[0]=_t_post_process[1]*mul; _t_post_process[1]=0;
- _t_gpu_wait [0]=_t_gpu_wait [1]/_t_measures[1]; _t_gpu_wait [1]=0; // '_t_gpu_wait' has it's own counter (_t_measures[1]) because it's called once per frame, while others can be called multiple times per frame
- _t_last_measure=t;
- _t_measures[0]=0;
- _t_measures[1]=0;
- }
- }
- }
- /******************************************************************************/
- void RendererClass::setMain() // !! requires 'D._lock' !! this is called after RT creation, and when VR GuiTexture is created/deleted/changed, and at the end of frame drawing for stereo mode (to advance to the next VR frame)
- {
- #if DX12
- map needs to be called for all images, cache the values, and call setMain in every frame
- #endif
- if(VR.active() && (_gui=VR.getNewGui()))
- {
- _gui_ds.getDS(_gui->w(), _gui->h());
- }else
- {
- _gui =&_main;
- _gui_ds=&_main_ds;
- }
- _cur_main =_gui .rc();
- _cur_main_ds=_gui_ds.rc();
- set(_cur_main, _cur_main_ds, false);
- }
- Bool RendererClass::mapMain()
- {
- return _main.map();
- }
- void RendererClass::unmapMain()
- {
- _main.unmap();
- _cur [0]=null;
- _cur_id[0]=NULL;
- }
- /******************************************************************************/
- Rect RendererClass::colClamp(C VecI2 &size)
- {
- Rect r((D.viewRect().min.x+D.w())*size.x/D.w2(), (D.h()-D.viewRect().max.y)*size.y/D.h2(),
- (D.viewRect().max.x+D.w())*size.x/D.w2(), (D.h()-D.viewRect().min.y)*size.y/D.h2());
- RectI ri=RoundGPU(r);
- r.min=(ri.min+0.5f)/size; // yes +0.5 is needed
- r.max=(ri.max-0.5f)/size; // yes -0.5 is needed
- return r;
- }
- Rect RendererClass::screenToPixel(C Rect &screen)
- {
- return Rect((screen.min.x+D.w())*Renderer.resW()/D.w2(), (D.h()-screen.max.y)*Renderer.resH()/D.h2(),
- (screen.max.x+D.w())*Renderer.resW()/D.w2(), (D.h()-screen.min.y)*Renderer.resH()/D.h2());
- }
- RectI RendererClass::screenToPixelI(C Rect &screen)
- {
- return RoundGPU(screenToPixel(screen));
- }
- Rect RendererClass::pixelToScreen(C RectI &pixel)
- {
- return Rect(pixel.min.x*D.w2()/Renderer.resW()-D.w(), D.h()-pixel.max.y*D.h2()/Renderer.resH(),
- pixel.max.x*D.w2()/Renderer.resW()-D.w(), D.h()-pixel.min.y*D.h2()/Renderer.resH());
- }
- Vec2 RendererClass::pixelToScreenSize(Flt pixel)
- {
- return Vec2(pixel*D.w2()/Renderer.resW(),
- pixel*D.h2()/Renderer.resH());
- }
- Vec2 RendererClass::screenToPixelSize(C Vec2 &screen)
- {
- return Vec2(screen.x*Renderer.resW()/D.w2(),
- screen.y*Renderer.resH()/D.h2());
- }
- /******************************************************************************/
- #if GL
- static void SwitchedFBO()
- {
- // update settings that depend on main FBO being active
- D.cullGL (); // adjust culling according to Y axis
- SetProjMatrix(); // flip Y 3D coords when Rendering To Texture
- }
- static inline Bool EqualRT(C Image *a, C Image *b)
- {
- UInt a_txtr, a_rb; if(a){a_txtr=a->_txtr; a_rb=a->_rb;}else a_txtr=a_rb=0;
- UInt b_txtr, b_rb; if(b){b_txtr=b->_txtr; b_rb=b->_rb;}else b_txtr=b_rb=0;
- return a_txtr==b_txtr && a_rb==b_rb;
- }
- static inline Bool EqualDS(C Image *a, C Image *b, UInt a_txtr)
- {
- UInt a_rb; if(a){ a_rb=a->_rb;}else a_rb=0;
- UInt b_txtr, b_rb; if(b){b_txtr=b->_txtr; b_rb=b->_rb;}else b_txtr=b_rb=0;
- return a_txtr==b_txtr && a_rb==b_rb;
- }
- static Bool EqualTxtr(C Image *a, C Image *b) {return (a ? a->_txtr : 0)==(b ? b->_txtr : 0);} // simpler version that checks texture ID's only, this can be used for #1+ RT's which never use RenderBuffers but only textures
- #endif
- #if DX9
- void RendererClass::setCube(Image &cube, Image *ds, DIR_ENUM dir)
- {
- if(cube._cube)
- {
- #if GL
- Bool was_main_fbo=D.mainFBO();
- #endif
- D3DCUBEMAP_FACES cf;
- switch(dir)
- {
- case DIR_LEFT : cf=D3DCUBEMAP_FACE_NEGATIVE_X; break;
- case DIR_RIGHT: cf=D3DCUBEMAP_FACE_POSITIVE_X; break;
- case DIR_DOWN : cf=D3DCUBEMAP_FACE_NEGATIVE_Y; break;
- case DIR_UP : cf=D3DCUBEMAP_FACE_POSITIVE_Y; break;
- case DIR_BACK : cf=D3DCUBEMAP_FACE_NEGATIVE_Z; break;
- default : cf=D3DCUBEMAP_FACE_POSITIVE_Z; break;
- }
- IDirect3DSurface9 *surf=null; cube._cube->GetCubeMapSurface(cf, 0, &surf);
- _cur [0]=&cube;
- _cur_id[0]= surf;
- D3D->SetRenderTarget(0, surf);
- RELEASE(surf);
- IDirect3DSurface9 *ids=(ds ? ds->_surf : null);
- if(_cur_ds_id!=ids){_cur_ds=ds; D3D->SetDepthStencilSurface(_cur_ds_id=ids);}
- _res.set(cube.w(), cube.h());
- if(Sh.h_RTSizeI)Sh.h_RTSizeI->set(Vec2(_res));
- #if DX9
- D.viewportForce(RectI(0, 0, resW(), resH())); // DX9 automatically sets full viewport on RT change, force current viewport values
- #elif GL
- if(was_main_fbo!=D.mainFBO())SwitchedFBO();
- #endif
- D._view_active.setRect(RectI(0, 0, resW(), resH())).setViewport(); // set full viewport
- D.clipAllow(_cur[0]==_cur_main);
- }
- }
- #endif
- #define R Renderer
- #if DX11
- void RendererClass::setDSLookup()
- {
- if(R._cur_ds)
- {
- R._cur_ds_ids[ NO_DEPTH_READ]=R._cur_ds-> _dsv;
- R._cur_ds_ids[NEED_DEPTH_READ]=R._cur_ds->_rdsv;
- R._cur_ds_ids[WANT_DEPTH_READ]=R._cur_ds_ids[R._cur_ds_ids[NEED_DEPTH_READ] ? NEED_DEPTH_READ : NO_DEPTH_READ]; // use RDSV if available, if not then DSV
- }else
- {
- R._cur_ds_ids[ NO_DEPTH_READ]=null;
- R._cur_ds_ids[WANT_DEPTH_READ]=null;
- R._cur_ds_ids[NEED_DEPTH_READ]=null;
- }
- }
- void RendererClass::setDS(ID3D11DepthStencilView *dsv)
- {
- if(R._cur_ds_id!=dsv)
- {
- if(dsv==R._cur_ds_ids[NO_DEPTH_READ])D.texClear(R._cur_ds->_srv); // if we're writing to depth then we need to unbind it from reading (because DirectX will do it)
- D3DC->OMSetRenderTargets(Elms(R._cur_id), R._cur_id, R._cur_ds_id=dsv);
- }
- }
- void RendererClass::needDepthTest() {if(D._depth_write || !R._cur_ds_id)setDS(R._cur_ds_ids[ NO_DEPTH_READ]);}
- void RendererClass::wantDepthRead() { setDS(R._cur_ds_ids[WANT_DEPTH_READ]);}
- void RendererClass::needDepthRead() { setDS(R._cur_ds_ids[NEED_DEPTH_READ]);}
- #elif WEB
- void RendererClass::setDSLookup()
- {
- /*R._cur_ds_ids[WANT_DEPTH_READ]=*/R._cur_ds_ids[NO_DEPTH_READ]=(R._cur_ds ? R._cur_ds->_txtr : 0); // WANT_DEPTH_READ will always be the same as NO_DEPTH_READ so never use it
- //R._cur_ds_ids[NEED_DEPTH_READ]=NULL; this will always be null so no need to change it, this is already cleared at startup, besides it's never accessed anyway
- }
- void RendererClass::setDS(UInt ds_txtr_id)
- {
- if(R._cur_ds_id!=ds_txtr_id)
- {
- R._cur_ds_id=ds_txtr_id;
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT , GL_TEXTURE_2D, ds_txtr_id, 0);
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, ds_txtr_id, 0); // this could fail if Image doesn't have stencil component
- }
- }
- void RendererClass::needDepthTest() {setDS(R._cur_ds_ids[NO_DEPTH_READ]);}
- #endif
- #undef R
- void RendererClass::set(Image *t0, Image *t1, Image *t2, Image *t3, Image *ds, Bool custom_viewport, DEPTH_READ_MODE depth_read_mode)
- {
- Int changed=0;
- #if DX9
- enum
- {
- RT0=0x1,
- RTN=0x2,
- DS =0x4,
- };
- IDirect3DSurface9 *id0=(t0 ? t0->_surf : null),
- *id1=(t1 ? t1->_surf : null),
- *id2=(t2 ? t2->_surf : null),
- *id3=(t3 ? t3->_surf : null),
- *ids=(ds ? ds->_surf : null);
- // a render target can't be attached to multiple slots, so that's why 1-2-3 slots need first to be detached
- if(_cur_id[1]!=id1 || _cur_id[2]!=id2 || _cur_id[3]!=id3)
- {
- if(_cur_id[3]){_cur[3]=null; changed|=RTN; D3D->SetRenderTarget(3, _cur_id[3]=null);}
- if(_cur_id[2]){_cur[2]=null; changed|=RTN; D3D->SetRenderTarget(2, _cur_id[2]=null);}
- if(_cur_id[1]){_cur[1]=null; changed|=RTN; D3D->SetRenderTarget(1, _cur_id[1]=null);}
- }
- if(_cur_id[0]!=id0){_cur[0]=t0; changed|=RT0; D3D->SetRenderTarget (0, _cur_id[0]=id0);}
- if(_cur_id[1]!=id1){_cur[1]=t1; changed|=RTN; D3D->SetRenderTarget (1, _cur_id[1]=id1);}
- if(_cur_id[2]!=id2){_cur[2]=t2; changed|=RTN; D3D->SetRenderTarget (2, _cur_id[2]=id2);}
- if(_cur_id[3]!=id3){_cur[3]=t3; changed|=RTN; D3D->SetRenderTarget (3, _cur_id[3]=id3);}
- if(_cur_ds_id!=ids){_cur_ds=ds; changed|=DS ; D3D->SetDepthStencilSurface( _cur_ds_id=ids);}
- #elif DX11
- ID3D11RenderTargetView *id0=(t0 ? t0->_rtv : null),
- *id1=(t1 ? t1->_rtv : null),
- *id2=(t2 ? t2->_rtv : null),
- *id3=(t3 ? t3->_rtv : null);
- ID3D11DepthStencilView *ids=(ds ? (depth_read_mode==NEED_DEPTH_READ
- || depth_read_mode==WANT_DEPTH_READ && ds->_rdsv) ? ds->_rdsv : ds->_dsv : null);
- if(_cur_id[0]!=id0 || _cur_id[1]!=id1 || _cur_id[2]!=id2 || _cur_id[3]!=id3 || _cur_ds_id!=ids)
- {
- if(id0 && _cur_id[0]!=id0)D.texClear(t0->_srv);
- if(id1 && _cur_id[1]!=id1)D.texClear(t1->_srv);
- if(id2 && _cur_id[2]!=id2)D.texClear(t2->_srv);
- if(id3 && _cur_id[3]!=id3)D.texClear(t3->_srv);
- if(ids && ids==ds->_dsv && _cur_ds_id!=ids)D.texClear(ds->_srv); // if we're writing to depth then we need to unbind it from reading (because DirectX will do it)
- changed=true;
- _cur[0]=t0; _cur_id[0]=id0;
- _cur[1]=t1; _cur_id[1]=id1;
- _cur[2]=t2; _cur_id[2]=id2;
- _cur[3]=t3; _cur_id[3]=id3;
- _cur_ds=ds; _cur_ds_id=ids;
- D3DC->OMSetRenderTargets(Elms(_cur_id), _cur_id, _cur_ds_id); ASSERT(ELMS(_cur_id)<=D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT);
- }else
- if(_cur_ds!=ds) // even if we're not changing RenderTargetView it's still possible we're changing RenderTarget Image, this can happen on DX10 when using NEED_DEPTH_READ, '_rdsv' null and last '_cur_ds_id' also null
- {
- changed=true;
- _cur_ds =ds;
- }
- #elif GL
- // !! '_cur_id' is not set for GL, only '_cur_ds_id' is !!
- Image *set_ds =((WEB && depth_read_mode==NEED_DEPTH_READ) ? null : ds); // if we require reading from the depth buffer, then we can't set it
- Bool was_main_fbo=D.mainFBO(),
- main_fbo=(t0==&_main || ds==&_main_ds), // check 'ds' and not 'set_ds' !!
- change_0, change_ds;
- if(main_fbo)
- {
- change_0 =((_cur[0]==&_main )!=(t0==&_main ));
- change_ds=((_cur_ds==&_main_ds)!=(ds==&_main_ds)); // check 'ds' and not 'set_ds' !!
- }else
- {
- change_0 =!EqualRT(_cur[0], t0);
- change_ds=!EqualDS(_cur_ds, set_ds, _cur_ds_id);
- }
- if(main_fbo!=was_main_fbo || change_0 || change_ds || !EqualTxtr(_cur[1], t1) || !EqualTxtr(_cur[2], t2) || !EqualTxtr(_cur[3], t3))
- {
- #if !IOS // there is no default frame buffer on iOS
- D.colWriteAllow((main_fbo && t0!=&_main) ? 0 : COL_WRITE_RGBA); // on desktop OpenGL and OpenGL ES (except iOS) '_main' is always linked with '_main_ds', when setting null RT and '_main_ds' DS, '_main' is set either way but with color writes disabled
- D. depthAllow(!main_fbo || ds==&_main_ds); // check 'ds' and not 'set_ds' !!
- if(main_fbo)
- {
- D.fbo(0); // set default frame buffer
- _cur_ds_id=0; // main FBO always has 0 depth txtr ID
- #if GL_ES // do this only on GLES, because on desktop it requires GL 4.3 TODO:
- if(D.notShaderModelGLES2()) // this check is needed because on GLES2 we don't have 'glInvalidateFramebuffer'
- {
- // discard, for main FBO we need to setup different values - https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glInvalidateFramebuffer.xhtml
- GLenum attachment[3]; GLsizei attachments=0; // RT0+Depth+Stencil
- if(_main ._discard){_main ._discard=false; attachment[attachments++]=GL_COLOR;}
- if(_main_ds._discard){_main_ds._discard=false; attachment[attachments++]=GL_DEPTH; if(ImageTI[_main_ds.hwType()].s)attachment[attachments++]=GL_STENCIL;}
- if(attachments)glInvalidateFramebuffer(GL_FRAMEBUFFER, attachments, attachment);
- }
- #endif
- }else
- #endif
- {
- #if !IOS // on iOS there's only one FBO and nothing else, so it is set during creation and not here
- D.fbo(FBO); // set custom frame buffer
- #endif
- #if IOS // on iOS there's only one FBO, so we can safely apply only changed RT's, otherwise we need to always set all of them
- if(change_ds)
- #endif
- {
- if(!set_ds)
- {
- _cur_ds_id=0;
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT , GL_RENDERBUFFER, 0);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
- }else
- if(_cur_ds_id=set_ds->_txtr)
- {
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT , GL_TEXTURE_2D, _cur_ds_id , 0);
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, ImageTI[set_ds->hwType()].s ? _cur_ds_id : 0, 0);
- }else
- {
- //_cur_ds_id=0; already set above
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT , GL_RENDERBUFFER, set_ds->_rb );
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, ImageTI[set_ds->hwType()].s ? set_ds->_rb : 0);
- }
- }
- #if IOS // on iOS there's only one FBO, so we can safely apply only changed RT's, otherwise we need to always set all of them
- if(change_0)
- #endif
- {
- if(!t0 )glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0);else
- if( t0->_txtr)glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D , t0->_txtr, 0);else
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, t0->_rb );
- }
- if(D.notShaderModelGLES2()) // this check is needed because on GLES2 we don't have MRT, and there's no 'glInvalidateFramebuffer'
- {
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, t1 ? t1->_txtr : 0, 0);
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, t2 ? t2->_txtr : 0, 0);
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, t3 ? t3->_txtr : 0, 0);
- GLenum buffers[]={GLenum(t0 ? GL_COLOR_ATTACHMENT0 : GL_NONE),
- GLenum(t1 ? GL_COLOR_ATTACHMENT1 : GL_NONE),
- GLenum(t2 ? GL_COLOR_ATTACHMENT2 : GL_NONE),
- GLenum(t3 ? GL_COLOR_ATTACHMENT3 : GL_NONE)};
- glDrawBuffers(Elms(buffers), buffers);
- glReadBuffer (buffers[0]);
- // discard
- #if GL_ES // do this only on GLES, because on desktop it requires GL 4.3 TODO:
- GLenum attachment[ELMS(_cur)+1]; GLsizei attachments=0; // RT's+DS
- if(t0 && t0->_discard){ t0->_discard=false; attachment[attachments++]=GL_COLOR_ATTACHMENT0;}
- if(t1 && t1->_discard){ t1->_discard=false; attachment[attachments++]=GL_COLOR_ATTACHMENT1;}
- if(t2 && t2->_discard){ t2->_discard=false; attachment[attachments++]=GL_COLOR_ATTACHMENT2;}
- if(t3 && t3->_discard){ t3->_discard=false; attachment[attachments++]=GL_COLOR_ATTACHMENT3;}
- if(set_ds && set_ds->_discard){set_ds->_discard=false; attachment[attachments++]=(ImageTI[set_ds->hwType()].s ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT);}
- if(attachments)glInvalidateFramebuffer(GL_FRAMEBUFFER, attachments, attachment);
- #endif
- }
- #if DEBUG
- GLenum error =glCheckFramebufferStatus(GL_FRAMEBUFFER);
- if( error!=GL_FRAMEBUFFER_COMPLETE)
- {
- LogN(S+"Invalid OpenGL FramebufferStatus: "+error+", frame:"+Time.frame()+", t0:"+(t0 ? t0->w() : 0)+", ds:"+(ds ? ds->w() : 0));
- Break();
- switch(error)
- {
- case GL_FRAMEBUFFER_UNSUPPORTED : error=0; break;
- case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT : error=0; break;
- case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: error=0; break;
- #if GL_ES
- case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS : error=0; break;
- #else
- case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER : error=0; break;
- case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER : error=0; break;
- #endif
- }
- }
- #endif
- }
- _cur[0] =t0;
- _cur[1] =t1;
- _cur[2] =t2;
- _cur[3] =t3;
- _cur_ds =ds;
- changed=true;
- }else
- if(_cur_ds!=ds) // check this in case 'set_ds' was cleared to null
- {
- _cur_ds =ds;
- changed=true;
- #if !IOS // there is no default frame buffer on iOS
- D.depthAllow(!main_fbo || ds==&_main_ds); // check 'ds' and not 'set_ds' !!
- #endif
- }
- #endif
- if(changed)
- {
- if(Image *main=(t0 ? t0 : ds))
- {
- _res=main->size();
- if(Sh.h_RTSizeI)Sh.h_RTSizeI->setConditional(_res);
- }
- #if DX9
- if(changed&RT0) // DX9 automatically performs some changes on RT #0 change (but not on the others, this was tested)
- {
- RectI full(0, 0, resW(), resH());
- D.viewportForce(full); // DX9 automatically sets full viewport, so we need to adjust cached viewport values to math those of the DX9
- D. clipForce(full); // DX9 automatically sets full clipping (scissor rect)
- }
- #elif GL
- if(was_main_fbo!=main_fbo)SwitchedFBO();
- #endif
- D._view_active.setRect(custom_viewport ? screenToPixelI(D.viewRect()) : RectI(0, 0, resW(), resH())).setViewport();
- D.clipAllow(_cur[0]==_cur_main);
- D.validateCoords(); // viewport was changed, also for DX9 (pixel offset) and OpenGL (flip Y 2D coords when Rendering To Texture)
- }else // render targets weren't changed, so set viewport only
- {
- RectI rect(custom_viewport ? screenToPixelI(D.viewRect()) : RectI(0, 0, resW(), resH()));
- if( rect!=D._view_active.recti) // over here we can do a quick != check first, because the Render Targets haven't changed (Renderer.resW(), resH() is the same, and that affects 'setRect')
- {
- D._view_active.setRect(rect).setViewport();
- D.validateCoords(); // viewport was changed, also for DX9 (pixel offset) and OpenGL (flip Y 2D coords when Rendering To Texture)
- }
- }
- }
- /******************************************************************************/
- void RendererClass::setMainViewport()
- {
- if(_stereo)
- {
- D._view_active.setRect(Renderer.screenToPixelI(D._view_rect)).setViewport(false).setShader();
- SetProjMatrix();
- SetCam(ActiveCam.matrix, false);
- D.validateCoords();
- D.setViewFovTan();
- }
- }
- void RendererClass::setEyeViewport()
- {
- if(_stereo)
- {
- D._view_active.setRect(Renderer.screenToPixelI(D._view_eye_rect[_eye])).setViewport(false).setShader(&ProjMatrixEyeOffset[_eye]); // 'setShader' needed for 'PosToScreen' and 'fur'
- SetProjMatrix(ProjMatrixEyeOffset[_eye]);
- SetCam(EyeMatrix[_eye], false);
- D.validateCoords(_eye);
- D.setViewFovTan();
- }
- }
- Rect* RendererClass::setEyeParams()
- {
- if(_stereo)
- {
- RectI rect=D._view_active.recti;
- D._view_active.setRect(Renderer.screenToPixelI(D._view_eye_rect[_eye])).setShader(&ProjMatrixEyeOffset[_eye]).setRect(rect);
- return &D._view_eye_rect[_eye];
- }
- return &D._view_rect;
- }
- /******************************************************************************/
- void RendererClass:: hasGlow() {_has_glow=true;}
- void RendererClass::finalizeGlow()
- {
- if(!ImageTI[_col->type()].a || !D.glowAllow() || !D.bloomAllow() || fastCombine())_has_glow=false; // glow can be done only if we have Alpha Channel in the RT, if we're allowing bloom processing (because it'd done together in the same shader), if we're allowing glow, and if 'fastCombine' is not active
- }
- /******************************************************************************/
- Bool RendererClass::capture(Image &image, Int w, Int h, Int type, Int mode, Int mip_maps, Bool alpha)
- {
- if(image.capture(_main))
- {
- if(type<=0)type=image.type();else MIN(type, IMAGE_TYPES);
- if(!_ds_1s)alpha=false;
- if(ImageTI[type].a && ImageTI[image.type()].a && !alpha && image.lock()) // dest has alpha and src has alpha, and don't want to manually set alpha
- {
- REPD(y, image.h())
- REPD(x, image.w())
- {
- Color color=image.color(x, y); color.a=255; // force full alpha
- image.color(x, y, color);
- }
- image.unlock();
- }
- if(image.copyTry(image, w, h, 1, type, mode, mip_maps))
- {
- if(alpha && ImageTI[image.type()].a && image.lock()) // set alpha from depth
- {
- Image depth; if(depth.capture(*_ds_1s) && depth.lockRead())
- {
- Image alpha(depth.w(), depth.h(), 1, IMAGE_A8, IMAGE_SOFT, 1);
- REPD(y, depth.h())
- REPD(x, depth.w())
- {
- Flt w=depth.pixelF(x, y);
- #if REVERSE_DEPTH
- alpha.pixB(x, y)=((w>0.0f) ? 0xFF : 0);
- #else
- alpha.pixB(x, y)=((w<1.0f) ? 0xFF : 0);
- #endif
- }
- depth.unlock();
- alpha.resize(image.w(), image.h(), FILTER_LINEAR);
- REPD(y, image.h())
- REPD(x, image.w())
- {
- Color color=image.color(x, y); color.a=alpha.pixB(x, y);
- image.color(x, y, color);
- }
- }else
- {
- REPD(y, image.h())
- REPD(x, image.w())
- {
- Color color=image.color(x, y); color.a=255; // force full alpha
- image.color(x, y, color);
- }
- }
- image.unlock().updateMipMaps();
- }
- return true;
- }
- }
- image.del(); return false;
- }
- Bool RendererClass::screenShot(C Str &name, Bool alpha)
- {
- FCreateDirs(GetPath(name));
- Image temp;
- if(alpha) // with alpha
- {
- if(capture(temp, -1, -1, IMAGE_DEFAULT, IMAGE_SOFT, 1, true))return temp.Export(name);
- }else
- if(temp.capture(_main)) // no alpha
- {
- if(ImageTI[temp.type()].a)temp.copyTry(temp, -1, -1, -1, IMAGE_R8G8B8, IMAGE_SOFT, 1); // if captured image has alpha channel then let's remove it
- return temp.Export(name);
- }
- return false;
- }
- Bool RendererClass::screenShots(C Str &name, C Str &ext, Bool alpha)
- {
- Str n=FFirst(name, ext);
- return n.is() ? screenShot(n, alpha) : false;
- }
- /******************************************************************************/
- void RendererClass::timeMeasure(Bool on)
- {
- if(_t_measure!=on)
- {
- _t_measure=on;
- _t_last_measure=Time.curTime();
- }
- }
- /******************************************************************************/
- }
- /******************************************************************************/
|