diff --git a/gfx/d3d9/d3d.cpp b/gfx/d3d9/d3d.cpp index 1b2e91fd82..b1b95a00d4 100644 --- a/gfx/d3d9/d3d.cpp +++ b/gfx/d3d9/d3d.cpp @@ -215,11 +215,11 @@ static void d3d_get_poke_interface(void *data, const video_poke_interface_t **if static void *d3d_init(const video_info_t *info, const input_driver_t **input, void **input_data) { - D3DVideo *vid = new D3DVideo(info, input, input_data); - - if (!vid) + D3DVideo *vid = new D3DVideo; + if (!vid->construct(info, input, input_data)) { RARCH_ERR("[D3D]: Failed to init D3D.\n"); + delete vid; return NULL; } diff --git a/gfx/d3d9/d3d9.hpp b/gfx/d3d9/d3d9.hpp index 6f6ad87dc9..ee91afc95a 100644 --- a/gfx/d3d9/d3d9.hpp +++ b/gfx/d3d9/d3d9.hpp @@ -17,6 +17,10 @@ #ifndef D3DVIDEO_HPP__ #define D3DVIDEO_HPP__ +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + #include "../../general.h" #include "../../driver.h" #include "../shader_parse.h" @@ -30,7 +34,6 @@ #include "d3d_defines.h" #include #include -#include //Needed for mingw class RenderChain; @@ -53,12 +56,16 @@ typedef struct class D3DVideo { public: - D3DVideo(const video_info_t* info, const input_driver_t **input, - void **input_data); + D3DVideo(); + ~D3DVideo(); + + // Delay constructor due to lack of exceptions. + bool construct(const video_info_t *info, + const input_driver_t **input, void **input_data); + bool frame(const void* frame, unsigned width, unsigned height, unsigned pitch, const char *msg); - ~D3DVideo(); bool alive(); bool focus() const; @@ -68,7 +75,7 @@ class D3DVideo bool read_viewport(uint8_t *buffer); void resize(unsigned new_width, unsigned new_height); bool set_shader(const std::string &path); - int process_shader(void); + bool process_shader(void); void set_filtering(unsigned index, bool smooth); void set_font_rect(font_params_t *params); @@ -121,8 +128,8 @@ class D3DVideo void process(void); - int init(const video_info_t *info); - int init_base(const video_info_t *info); + bool init(const video_info_t *info); + bool init_base(const video_info_t *info); void make_d3dpp(const video_info_t *info, D3DPRESENT_PARAMETERS *d3dpp); void deinit(void); RECT monitor_rect(void); @@ -137,12 +144,11 @@ class D3DVideo void deinit_cg(); #endif - int init_imports(void); - void init_luts(void); - int init_singlepass(void); - int init_multipass(void); + bool init_imports(void); + bool init_luts(void); + bool init_singlepass(void); + bool init_multipass(void); bool init_chain(const video_info_t *video_info); - std::unique_ptr chain; void deinit_chain(void); bool init_font(void); @@ -164,6 +170,8 @@ class D3DVideo #ifdef HAVE_MENU overlay_t rgui; #endif + + RenderChain *chain; }; #endif diff --git a/gfx/d3d9/d3d9_pc.cpp b/gfx/d3d9/d3d9_pc.cpp index 42e24580e3..c8bdfac23d 100644 --- a/gfx/d3d9/d3d9_pc.cpp +++ b/gfx/d3d9/d3d9_pc.cpp @@ -143,7 +143,7 @@ RECT D3DVideo::monitor_rect(void) } #endif -int D3DVideo::init_base(const video_info_t *info) +bool D3DVideo::init_base(const video_info_t *info) { D3DPRESENT_PARAMETERS d3dpp; make_d3dpp(info, &d3dpp); @@ -152,7 +152,7 @@ int D3DVideo::init_base(const video_info_t *info) if (!g_pD3D) { RARCH_ERR("Failed to create D3D interface!\n"); - return 1; + return false; } if (FAILED(Callback::d3d_err = g_pD3D->CreateDevice( @@ -175,11 +175,11 @@ int D3DVideo::init_base(const video_info_t *info) &dev))) { RARCH_ERR("Failed to initialize device.\n"); - return 1; + return false; } } - return 0; + return true; } void D3DVideo::make_d3dpp(const video_info_t *info, D3DPRESENT_PARAMETERS *d3dpp) @@ -214,9 +214,9 @@ void D3DVideo::make_d3dpp(const video_info_t *info, D3DPRESENT_PARAMETERS *d3dpp } } -int D3DVideo::init(const video_info_t *info) +bool D3DVideo::init(const video_info_t *info) { - int ret = 0; + bool ret = true; if (!g_pD3D) ret = init_base(info); else if (needs_restore) @@ -250,14 +250,14 @@ int D3DVideo::init(const video_info_t *info) g_pD3D->Release(); g_pD3D = NULL; ret = init_base(info); - if (!ret) + if (ret) RARCH_LOG("[D3D]: Recovered from dead state.\n"); else return ret; } } - if (ret) + if (!ret) return ret; calculate_rect(screen_width, screen_height, info->force_aspect, g_extern.system.aspect_ratio); @@ -266,28 +266,37 @@ int D3DVideo::init(const video_info_t *info) if (!init_cg()) { RARCH_ERR("Failed to initialize Cg.\n"); - return 1; + return false; } #endif + if (!init_chain(info)) { RARCH_ERR("Failed to initialize render chain.\n"); - return 1; + return false; } + if (!init_font()) { RARCH_ERR("Failed to initialize font.\n"); - return 1; + return false; } - return 0; + return true; } void D3DVideo::set_viewport(int x, int y, unsigned width, unsigned height) { D3DVIEWPORT viewport; - viewport.X = max(x, 0); // D3D9 doesn't support negative X/Y viewports ... - viewport.Y = max(y, 0); + + // D3D9 doesn't support negative X/Y viewports ... + if (x < 0) + x = 0; + if (y < 0) + y = 0; + + viewport.X = x; + viewport.Y = y; viewport.Width = width; viewport.Height = height; viewport.MinZ = 0.0f; @@ -440,12 +449,21 @@ void D3DVideo::calculate_rect(unsigned width, unsigned height, } } -D3DVideo::D3DVideo(const video_info_t *info, const input_driver_t **input, - void **input_data) : +D3DVideo::D3DVideo() : g_pD3D(NULL), dev(NULL), font(NULL), - rotation(0), needs_restore(false), cgCtx(NULL), overlays_enabled(false) + rotation(0), needs_restore(false), +#ifdef HAVE_CG + cgCtx(NULL), +#endif +#ifdef HAVE_OVERLAY + overlays_enabled(false), +#endif + chain(NULL) +{} + +bool D3DVideo::construct(const video_info_t *info, const input_driver_t **input, + void **input_data) { - int ret = 0; should_resize = false; gfx_set_dwm(); @@ -536,10 +554,12 @@ D3DVideo::D3DVideo(const video_info_t *info, const input_driver_t **input, cg_shader = g_settings.video.shader_path; #endif - process_shader(); + if (!process_shader()) + return false; video_info = *info; - ret = init(&video_info); + if (!init(&video_info)) + return false; if (input && input_data) { @@ -549,6 +569,7 @@ D3DVideo::D3DVideo(const video_info_t *info, const input_driver_t **input, } RARCH_LOG("[D3D]: Init complete.\n"); + return true; } void D3DVideo::deinit(void) @@ -600,7 +621,7 @@ D3DVideo::~D3DVideo(void) bool D3DVideo::restore(void) { deinit(); - needs_restore = init(&video_info); + needs_restore = !init(&video_info); if (needs_restore) RARCH_ERR("[D3D]: Restore error.\n"); @@ -652,12 +673,19 @@ bool D3DVideo::frame(const void *frame, { if (dev->Present(NULL, NULL, NULL, NULL) != D3D_OK) { + RARCH_ERR("[D3D]: Present() failed.\n"); needs_restore = true; return true; } dev->Clear(0, 0, D3DCLEAR_TARGET, 0, 1, 0); } + if (!chain) + { + RARCH_ERR("[D3D]: Render chain is missing!.\n"); + return false; + } + if (!chain->render(frame, width, height, pitch, rotation)) { RARCH_ERR("[D3D]: Failed to render scene.\n"); @@ -684,6 +712,7 @@ bool D3DVideo::frame(const void *frame, if (dev->Present(NULL, NULL, NULL, NULL) != D3D_OK) { needs_restore = true; + RARCH_ERR("[D3D]: Present() failed.\n"); return true; } @@ -776,7 +805,7 @@ void D3DVideo::deinit_cg(void) } #endif -int D3DVideo::init_singlepass(void) +bool D3DVideo::init_singlepass(void) { memset(&shader, 0, sizeof(shader)); shader.passes = 1; @@ -786,13 +815,13 @@ int D3DVideo::init_singlepass(void) pass.fbo.type_x = pass.fbo.type_y = RARCH_SCALE_VIEWPORT; strlcpy(pass.source.cg, cg_shader.c_str(), sizeof(pass.source.cg)); - return 0; + return true; } -int D3DVideo::init_imports(void) +bool D3DVideo::init_imports(void) { if (!shader.variables) - return 0; + return true; state_tracker_info tracker_info = {0}; @@ -814,7 +843,7 @@ int D3DVideo::init_imports(void) if (!state_tracker) { RARCH_ERR("Failed to initialize state tracker.\n"); - return 1; + return false; } std::shared_ptr tracker(state_tracker, [](state_tracker_t *tracker) { @@ -823,27 +852,32 @@ int D3DVideo::init_imports(void) chain->add_state_tracker(tracker); - return 0; + return true; } -void D3DVideo::init_luts(void) +bool D3DVideo::init_luts(void) { for (unsigned i = 0; i < shader.luts; i++) { - chain->add_lut(shader.lut[i].id, shader.lut[i].path, + bool ret = chain->add_lut(shader.lut[i].id, shader.lut[i].path, shader.lut[i].filter == RARCH_FILTER_UNSPEC ? g_settings.video.smooth : (shader.lut[i].filter == RARCH_FILTER_LINEAR)); + + if (!ret) + return ret; } + + return true; } -int D3DVideo::init_multipass(void) +bool D3DVideo::init_multipass(void) { config_file_t *conf = config_file_new(cg_shader.c_str()); if (!conf) { RARCH_ERR("Failed to load preset.\n"); - return 1; + return false; } memset(&shader, 0, sizeof(shader)); @@ -852,7 +886,7 @@ int D3DVideo::init_multipass(void) { config_file_free(conf); RARCH_ERR("Failed to parse CGP file.\n"); - return 1; + return false; } config_file_free(conf); @@ -886,22 +920,18 @@ int D3DVideo::init_multipass(void) pass.fbo.type_x = pass.fbo.type_y = RARCH_SCALE_VIEWPORT; } - return 0; + return true; } bool D3DVideo::set_shader(const std::string &path) { auto old_shader = cg_shader; bool restore_old = false; - try + cg_shader = path; + + if (!process_shader() || !restore()) { - cg_shader = path; - process_shader(); - restore(); - } - catch (const std::exception &e) - { - RARCH_ERR("[D3D9]: Setting shader failed: (%s).\n", e.what()); + RARCH_ERR("[D3D9]: Setting shader failed.\n"); restore_old = true; } @@ -915,7 +945,7 @@ bool D3DVideo::set_shader(const std::string &path) return !restore_old; } -int D3DVideo::process_shader(void) +bool D3DVideo::process_shader(void) { if (strcmp(path_get_extension(cg_shader.c_str()), "cgp") == 0) return init_multipass(); @@ -925,57 +955,63 @@ int D3DVideo::process_shader(void) void D3DVideo::recompute_pass_sizes(void) { - try + LinkInfo link_info = {0}; + link_info.pass = &shader.pass[0]; + link_info.tex_w = link_info.tex_h = video_info.input_scale * RARCH_SCALE_BASE; + + unsigned current_width = link_info.tex_w; + unsigned current_height = link_info.tex_h; + unsigned out_width = 0; + unsigned out_height = 0; + + if (!chain->set_pass_size(0, current_width, current_height)) { - LinkInfo link_info = {0}; - link_info.pass = &shader.pass[0]; - link_info.tex_w = link_info.tex_h = video_info.input_scale * RARCH_SCALE_BASE; - - unsigned current_width = link_info.tex_w; - unsigned current_height = link_info.tex_h; - unsigned out_width = 0; - unsigned out_height = 0; - - chain->set_pass_size(0, current_width, current_height); - for (unsigned i = 1; i < shader.passes; i++) - { - RenderChain::convert_geometry(link_info, - out_width, out_height, - current_width, current_height, final_viewport); - - link_info.tex_w = next_pow2(out_width); - link_info.tex_h = next_pow2(out_height); - - chain->set_pass_size(i, link_info.tex_w, link_info.tex_h); - - current_width = out_width; - current_height = out_height; - - link_info.pass = &shader.pass[i]; - } + RARCH_ERR("[D3D]: Failed to set pass size.\n"); + return; } - catch (const std::exception& e) + + for (unsigned i = 1; i < shader.passes; i++) { - RARCH_ERR("[D3D9]: Render chain error: (%s).\n", e.what()); + RenderChain::convert_geometry(link_info, + out_width, out_height, + current_width, current_height, final_viewport); + + link_info.tex_w = next_pow2(out_width); + link_info.tex_h = next_pow2(out_height); + + if (!chain->set_pass_size(i, link_info.tex_w, link_info.tex_h)) + { + RARCH_ERR("[D3D]: Failed to set pass size.\n"); + return; + } + + current_width = out_width; + current_height = out_height; + + link_info.pass = &shader.pass[i]; } } bool D3DVideo::init_chain(const video_info_t *video_info) { - int ret = 0; // Setup information for first pass. LinkInfo link_info = {0}; link_info.pass = &shader.pass[0]; link_info.tex_w = link_info.tex_h = video_info->input_scale * RARCH_SCALE_BASE; - chain = std::unique_ptr( - new RenderChain( - video_info, - dev, cgCtx, - link_info, - video_info->rgb32 ? RenderChain::ARGB : RenderChain::RGB565, - final_viewport)); + delete chain; + chain = new RenderChain( + video_info, + dev, cgCtx, + final_viewport); + + if (!chain->init(link_info, + video_info->rgb32 ? RenderChain::ARGB : RenderChain::RGB565)) + { + RARCH_ERR("[D3D9]: Failed to init render chain.\n"); + return false; + } unsigned current_width = link_info.tex_w; unsigned current_height = link_info.tex_h; @@ -995,15 +1031,22 @@ bool D3DVideo::init_chain(const video_info_t *video_info) current_width = out_width; current_height = out_height; - chain->add_pass(link_info); + if (!chain->add_pass(link_info)) + { + RARCH_ERR("[D3D9]: Failed to add pass.\n"); + return false; + } } - init_luts(); - ret = init_imports(); - - if (ret) + if (!init_luts()) { - RARCH_ERR("[D3D9]: Render chain error.\n"); + RARCH_ERR("[D3D9]: Failed to init LUTs.\n"); + return false; + } + + if (!init_imports()) + { + RARCH_ERR("[D3D9]: Failed to init imports.\n"); return false; } @@ -1012,7 +1055,8 @@ bool D3DVideo::init_chain(const video_info_t *video_info) void D3DVideo::deinit_chain(void) { - chain.reset(); + delete chain; + chain = NULL; } bool D3DVideo::init_font(void) diff --git a/gfx/d3d9/render_chain.cpp b/gfx/d3d9/render_chain.cpp index 9d6bd4c7d1..51e5ca122c 100644 --- a/gfx/d3d9/render_chain.cpp +++ b/gfx/d3d9/render_chain.cpp @@ -62,14 +62,20 @@ RenderChain::~RenderChain() RenderChain::RenderChain(const video_info_t *video_info, LPDIRECT3DDEVICE dev_, CGcontext cgCtx_, - const LinkInfo &info, PixelFormat fmt, const D3DVIEWPORT &final_viewport_) : dev(dev_), cgCtx(cgCtx_), video_info(*video_info), final_viewport(final_viewport_), frame_count(0) +{} + +bool RenderChain::init(const LinkInfo &info, PixelFormat fmt) { pixel_size = fmt == RGB565 ? 2 : 4; - create_first_pass(info, fmt); + if (!create_first_pass(info, fmt)) + return false; log_info(info); - compile_shaders(fStock, vStock, ""); + if (!compile_shaders(fStock, vStock, "")) + return false; + + return true; } void RenderChain::clear() @@ -113,7 +119,7 @@ void RenderChain::set_final_viewport(const D3DVIEWPORT9& final_viewport) this->final_viewport = final_viewport; } -void RenderChain::set_pass_size(unsigned pass_index, unsigned width, unsigned height) +bool RenderChain::set_pass_size(unsigned pass_index, unsigned width, unsigned height) { Pass &pass = passes[pass_index]; if (width != pass.info.tex_w || height != pass.info.tex_h) @@ -128,7 +134,7 @@ void RenderChain::set_pass_size(unsigned pass_index, unsigned width, unsigned he D3DPOOL_DEFAULT, &pass.tex, NULL))) { - throw std::runtime_error("Failed to create texture ..."); + return false; } dev->SetTexture(0, pass.tex); @@ -136,9 +142,11 @@ void RenderChain::set_pass_size(unsigned pass_index, unsigned width, unsigned he dev->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER); dev->SetTexture(0, NULL); } + + return true; } -void RenderChain::add_pass(const LinkInfo &info) +bool RenderChain::add_pass(const LinkInfo &info) { Pass pass; pass.info = info; @@ -146,7 +154,8 @@ void RenderChain::add_pass(const LinkInfo &info) pass.last_height = 0; compile_shaders(pass.fPrg, pass.vPrg, info.pass->source.cg); - init_fvf(pass); + if (!init_fvf(pass)) + return false; if (FAILED(dev->CreateVertexBuffer( 4 * sizeof(Vertex), @@ -156,7 +165,7 @@ void RenderChain::add_pass(const LinkInfo &info) &pass.vertex_buf, NULL))) { - throw std::runtime_error("Failed to create Vertex buf ..."); + return false; } if (FAILED(dev->CreateTexture(info.tex_w, info.tex_h, 1, @@ -165,7 +174,7 @@ void RenderChain::add_pass(const LinkInfo &info) D3DPOOL_DEFAULT, &pass.tex, NULL))) { - throw std::runtime_error("Failed to create texture ..."); + return false; } dev->SetTexture(0, pass.tex); @@ -176,9 +185,10 @@ void RenderChain::add_pass(const LinkInfo &info) passes.push_back(pass); log_info(info); + return true; } -void RenderChain::add_lut(const std::string &id, +bool RenderChain::add_lut(const std::string &id, const std::string &path, bool smooth) { @@ -202,7 +212,7 @@ void RenderChain::add_lut(const std::string &id, NULL, &lut))) { - throw std::runtime_error("Failed to load LUT!"); + return false; } dev->SetTexture(0, lut); @@ -212,6 +222,7 @@ void RenderChain::add_lut(const std::string &id, lut_info info = { lut, id, smooth }; luts.push_back(info); + return true; } void RenderChain::add_state_tracker(std::shared_ptr tracker) @@ -327,7 +338,7 @@ D3DTEXTUREFILTERTYPE RenderChain::translate_filter(bool smooth) return smooth ? D3DTEXF_LINEAR : D3DTEXF_POINT; } -void RenderChain::create_first_pass(const LinkInfo &info, PixelFormat fmt) +bool RenderChain::create_first_pass(const LinkInfo &info, PixelFormat fmt) { D3DXMATRIX ident; D3DXMatrixIdentity(&ident); @@ -353,7 +364,7 @@ void RenderChain::create_first_pass(const LinkInfo &info, PixelFormat fmt) &prev.vertex_buf[i], NULL))) { - throw std::runtime_error("Failed to create Vertex buf ..."); + return false; } if (FAILED(dev->CreateTexture(info.tex_w, info.tex_h, 1, 0, @@ -361,7 +372,7 @@ void RenderChain::create_first_pass(const LinkInfo &info, PixelFormat fmt) D3DPOOL_MANAGED, &prev.tex[i], NULL))) { - throw std::runtime_error("Failed to create texture ..."); + return false; } dev->SetTexture(0, prev.tex[i]); @@ -375,11 +386,13 @@ void RenderChain::create_first_pass(const LinkInfo &info, PixelFormat fmt) } compile_shaders(pass.fPrg, pass.vPrg, info.pass->source.cg); - init_fvf(pass); + if (!init_fvf(pass)) + return false; passes.push_back(pass); + return true; } -void RenderChain::compile_shaders(CGprogram &fPrg, CGprogram &vPrg, const std::string &shader) +bool RenderChain::compile_shaders(CGprogram &fPrg, CGprogram &vPrg, const std::string &shader) { CGprofile vertex_profile = cgD3D9GetLatestVertexProfile(); CGprofile fragment_profile = cgD3D9GetLatestPixelProfile(); @@ -421,10 +434,11 @@ void RenderChain::compile_shaders(CGprogram &fPrg, CGprogram &vPrg, const std::s } if (!fPrg || !vPrg) - throw std::runtime_error("Failed to compile shaders!"); + return false; cgD3D9LoadProgram(fPrg, true, 0); cgD3D9LoadProgram(vPrg, true, 0); + return true; } void RenderChain::set_shaders(CGprogram &fPrg, CGprogram &vPrg) @@ -996,7 +1010,7 @@ static inline CGparameter find_param_from_semantic(CGprogram prog, const std::st { (WORD)(stream), (WORD)(offset * sizeof(float)), D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, \ D3DDECLUSAGE_COLOR, (BYTE)(index) } \ -void RenderChain::init_fvf(Pass &pass) +bool RenderChain::init_fvf(Pass &pass) { static const D3DVERTEXELEMENT decl_end = D3DDECL_END(); static const D3DVERTEXELEMENT position_decl = DECL_FVF_POSITION(0); @@ -1006,7 +1020,7 @@ void RenderChain::init_fvf(Pass &pass) D3DVERTEXELEMENT decl[MAXD3DDECLLENGTH] = {{0}}; if (cgD3D9GetVertexDeclaration(pass.vPrg, decl) == CG_FALSE) - throw std::runtime_error("Failed to get VertexDeclaration!"); + return false; unsigned count; for (count = 0; count < MAXD3DDECLLENGTH; count++) @@ -1112,7 +1126,9 @@ void RenderChain::init_fvf(Pass &pass) } if (FAILED(dev->CreateVertexDeclaration(decl, &pass.vertex_decl))) - throw std::runtime_error("Failed to set up FVF!"); + return false; + + return true; } void RenderChain::bind_tracker(Pass &pass, unsigned pass_index) diff --git a/gfx/d3d9/render_chain.hpp b/gfx/d3d9/render_chain.hpp index b1e515b1b2..e5e8127382 100644 --- a/gfx/d3d9/render_chain.hpp +++ b/gfx/d3d9/render_chain.hpp @@ -47,14 +47,15 @@ class RenderChain #ifdef HAVE_CG CGcontext cgCtx, #endif - const LinkInfo &info, - PixelFormat fmt, const D3DVIEWPORT &final_viewport); + ~RenderChain(); - void set_pass_size(unsigned pass, unsigned width, unsigned height); + bool init(const LinkInfo &info, PixelFormat fmt); + + bool set_pass_size(unsigned pass, unsigned width, unsigned height); void set_final_viewport(const D3DVIEWPORT9 &final_viewport); - void add_pass(const LinkInfo &info); - void add_lut(const std::string &id, const std::string &path, bool smooth); + bool add_pass(const LinkInfo &info); + bool add_lut(const std::string &id, const std::string &path, bool smooth); void add_state_tracker(std::shared_ptr tracker); bool render(const void *data, @@ -66,11 +67,10 @@ class RenderChain const D3DVIEWPORT &final_viewport); void clear(); - ~RenderChain(); private: - LPDIRECT3DDEVICE dev; + LPDIRECT3DDEVICE dev; #ifdef HAVE_CG CGcontext cgCtx; #endif @@ -121,8 +121,8 @@ class RenderChain D3DVIEWPORT final_viewport; unsigned frame_count; - void create_first_pass(const LinkInfo &info, PixelFormat fmt); - void compile_shaders(CGprogram &fPrg, CGprogram &vPrg, const std::string &shader); + bool create_first_pass(const LinkInfo &info, PixelFormat fmt); + bool compile_shaders(CGprogram &fPrg, CGprogram &vPrg, const std::string &shader); void set_vertices(Pass &pass, unsigned width, unsigned height, @@ -164,7 +164,7 @@ class RenderChain void bind_tracker(Pass &pass, unsigned pass_index); void unbind_all(); - void init_fvf(Pass &pass); + bool init_fvf(Pass &pass); }; #endif