static inline int find_num_frames(int rate, int latency) { int frames = (rate * latency) / 1000; // SDL only likes 2^n sized buffers. return next_pow2(frames); }
static bool psp_frame(void *data, const void *frame, unsigned width, unsigned height, uint64_t frame_count, unsigned pitch, const char *msg) { #ifdef DISPLAY_FPS uint32_t diff; static uint64_t currentTick,lastTick; static int frames; static float fps = 0.0; #endif static struct retro_perf_counter psp_frame_run = {0}; static char fps_txt[128] = {0}; static char fps_text_buf[128] = {0}; psp1_video_t *psp = (psp1_video_t*)data; settings_t *settings = config_get_ptr(); if (!width || !height) return false; if (((uint32_t)frame&0x04000000) || (frame == RETRO_HW_FRAME_BUFFER_VALID)) psp->hw_render = true; else if (frame) psp->hw_render = false; if (!psp->hw_render) sceGuSync(0, 0); /* let the core decide when to sync when HW_RENDER */ pspDebugScreenSetBase(psp->draw_buffer); pspDebugScreenSetXY(0,0); video_monitor_get_fps(fps_txt, sizeof(fps_txt), settings->fps_show ? fps_text_buf : NULL, settings->fps_show ? sizeof(fps_text_buf) : 0); if(settings->fps_show) { pspDebugScreenSetXY(68 - strlen(fps_text_buf) - 1,0); pspDebugScreenPuts(fps_text_buf); pspDebugScreenSetXY(0,1); } if (msg) pspDebugScreenPuts(msg); if ((psp->vsync)&&(psp->vblank_not_reached)) sceDisplayWaitVblankStart(); psp->vblank_not_reached = true; #ifdef DISPLAY_FPS frames++; sceRtcGetCurrentTick(¤tTick); diff = currentTick - lastTick; if(diff > 1000000) { fps = (float)frames * 1000000.0 / diff; lastTick = currentTick; frames = 0; } pspDebugScreenSetXY(0,0); pspDebugScreenPrintf("%f", fps); #endif psp->draw_buffer = FROM_GU_POINTER(sceGuSwapBuffers()); rarch_perf_init(&psp_frame_run, "psp_frame_run"); retro_perf_start(&psp_frame_run); if (psp->should_resize) psp_update_viewport(psp); psp_set_tex_coords(psp->frame_coords, width, height); sceGuStart(GU_DIRECT, psp->main_dList); sceGuTexFilter(psp->tex_filter, psp->tex_filter); sceGuClear(GU_COLOR_BUFFER_BIT); /* frame in VRAM ? texture/palette was * set in core so draw directly */ if (psp->hw_render) sceGuDrawArray(GU_SPRITES, GU_TEXTURE_32BITF | GU_VERTEX_32BITF | GU_TRANSFORM_2D, PSP_FRAME_VERTEX_COUNT, NULL, (void*)(psp->frame_coords)); else { if (frame) { sceKernelDcacheWritebackRange(frame,pitch * height); sceGuCopyImage(psp->rgb32? GU_PSM_8888 : GU_PSM_5650, ((u32)frame & 0xF) >> psp->bpp_log2, 0, width, height, pitch >> psp->bpp_log2, (void*)((u32)frame & ~0xF), 0, 0, width, psp->texture); } sceGuTexImage(0, next_pow2(width), next_pow2(height), width, psp->texture); sceGuCallList(psp->frame_dList); } sceGuFinish(); retro_perf_stop(&psp_frame_run); if(psp->menu.active) { sceGuSendList(GU_TAIL, psp->menu.dList, &(psp->menu.context_storage)); sceGuSync(0, 0); } return true; }
static bool psp_frame(void *data, const void *frame, unsigned width, unsigned height, unsigned pitch, const char *msg) { static char fps_txt[128], fps_text_buf[128]; psp1_video_t *psp = (psp1_video_t*)data; #ifdef DISPLAY_FPS static uint64_t currentTick,lastTick; static float fps=0.0; static int frames; #endif if (!width || !height) return false; if (((uint32_t)frame&0x04000000) || (frame == RETRO_HW_FRAME_BUFFER_VALID)) psp->hw_render = true; else if (frame) psp->hw_render = false; if (!psp->hw_render) sceGuSync(0, 0); // let the core decide when to sync when HW_RENDER pspDebugScreenSetBase(psp->draw_buffer); pspDebugScreenSetXY(0,0); if(g_settings.fps_show) { gfx_get_fps(fps_txt, sizeof(fps_txt), fps_text_buf, sizeof(fps_text_buf)); pspDebugScreenSetXY(68 - strlen(fps_text_buf) - 1,0); pspDebugScreenPuts(fps_text_buf); pspDebugScreenSetXY(0,1); } else gfx_get_fps(fps_txt, sizeof(fps_txt), NULL, 0); if (msg) pspDebugScreenPuts(msg); if ((psp->vsync)&&(psp->vblank_not_reached)) sceDisplayWaitVblankStart(); psp->vblank_not_reached = true; #ifdef DISPLAY_FPS frames++; sceRtcGetCurrentTick(¤tTick); uint32_t diff = currentTick - lastTick; if(diff > 1000000) { fps = (float)frames * 1000000.0 / diff; lastTick = currentTick; frames = 0; } pspDebugScreenSetXY(0,0); pspDebugScreenPrintf("%f", fps); #endif psp->draw_buffer = FROM_GU_POINTER(sceGuSwapBuffers()); g_extern.frame_count++; RARCH_PERFORMANCE_INIT(psp_frame_run); RARCH_PERFORMANCE_START(psp_frame_run); if (psp->should_resize) psp_update_viewport(psp); psp_set_tex_coords(psp->frame_coords, width, height); sceGuStart(GU_DIRECT, psp->main_dList); sceGuTexFilter(psp->tex_filter, psp->tex_filter); sceGuClear(GU_COLOR_BUFFER_BIT); if (psp->hw_render) // frame in VRAM ? texture/palette was set in core so draw directly sceGuDrawArray(GU_SPRITES, GU_TEXTURE_32BITF | GU_VERTEX_32BITF | GU_TRANSFORM_2D, PSP_FRAME_VERTEX_COUNT, NULL, (void*)(psp->frame_coords)); else { if (frame!=NULL) { sceKernelDcacheWritebackRange(frame,pitch * height); sceGuCopyImage(GU_PSM_5650, ((u32)frame & 0xF) >> psp->bpp_log2, 0, width, height, pitch >> psp->bpp_log2, (void*)((u32)frame & ~0xF), 0, 0, width, psp->texture); } sceGuTexImage(0, next_pow2(width), next_pow2(height), width, psp->texture); sceGuCallList(psp->frame_dList); }
void init_video_input(void) { rarch_init_filter(g_extern.system.pix_fmt); init_shader_dir(); const struct retro_game_geometry *geom = &g_extern.system.av_info.geometry; unsigned max_dim = max(geom->max_width, geom->max_height); unsigned scale = next_pow2(max_dim) / RARCH_SCALE_BASE; scale = max(scale, 1); if (g_extern.filter.filter) scale = g_extern.filter.scale; // Update core-dependent aspect ratio values. gfx_set_square_pixel_viewport(geom->base_width, geom->base_height); gfx_set_core_viewport(); gfx_set_config_viewport(); // Update CUSTOM viewport. rarch_viewport_t *custom_vp = &g_extern.console.screen.viewports.custom_vp; if (g_settings.video.aspect_ratio_idx == ASPECT_RATIO_CUSTOM) { float default_aspect = aspectratio_lut[ASPECT_RATIO_CORE].value; aspectratio_lut[ASPECT_RATIO_CUSTOM].value = (custom_vp->width && custom_vp->height) ? (float)custom_vp->width / custom_vp->height : default_aspect; } g_extern.system.aspect_ratio = aspectratio_lut[g_settings.video.aspect_ratio_idx].value; unsigned width; unsigned height; if (g_settings.video.fullscreen) { width = g_settings.video.fullscreen_x; height = g_settings.video.fullscreen_y; } else { if (g_settings.video.force_aspect) { // Do rounding here to simplify integer scale correctness. unsigned base_width = roundf(geom->base_height * g_extern.system.aspect_ratio); width = roundf(base_width * g_settings.video.scale); height = roundf(geom->base_height * g_settings.video.scale); } else { width = roundf(geom->base_width * g_settings.video.scale); height = roundf(geom->base_height * g_settings.video.scale); } } if (width && height) RARCH_LOG("Video @ %ux%u\n", width, height); else RARCH_LOG("Video @ fullscreen\n"); driver.display_type = RARCH_DISPLAY_NONE; driver.video_display = 0; driver.video_window = 0; if (!init_video_pixel_converter(RARCH_SCALE_BASE * scale)) { RARCH_ERR("Failed to initialize pixel converter.\n"); rarch_fail(1, "init_video_input()"); } video_info_t video = {0}; video.width = width; video.height = height; video.fullscreen = g_settings.video.fullscreen; video.vsync = g_settings.video.vsync && !g_extern.system.force_nonblock; video.force_aspect = g_settings.video.force_aspect; #ifdef GEKKO video.viwidth = g_settings.video.viwidth; #endif video.smooth = g_settings.video.smooth; video.input_scale = scale; video.rgb32 = g_extern.filter.filter ? g_extern.filter.out_rgb32 : (g_extern.system.pix_fmt == RETRO_PIXEL_FORMAT_XRGB8888); const input_driver_t *tmp = driver.input; find_video_driver(); // Need to grab the "real" video driver interface on a reinit. #ifdef HAVE_THREADS if (g_settings.video.threaded && !g_extern.system.hw_render_callback.context_type) // Can't do hardware rendering with threaded driver currently. { RARCH_LOG("Starting threaded video driver ...\n"); if (!rarch_threaded_video_init(&driver.video, &driver.video_data, &driver.input, &driver.input_data, driver.video, &video)) { RARCH_ERR("Cannot open threaded video driver ... Exiting ...\n"); rarch_fail(1, "init_video_input()"); } } else #endif driver.video_data = driver.video->init(&video, &driver.input, &driver.input_data); if (!driver.video_data) { RARCH_ERR("Cannot open video driver ... Exiting ...\n"); rarch_fail(1, "init_video_input()"); } driver.video_poke = NULL; if (driver.video->poke_interface) driver.video->poke_interface(driver.video_data, &driver.video_poke); // Force custom viewport to have sane parameters. if (driver.video->viewport_info && (!custom_vp->width || !custom_vp->height)) { custom_vp->width = width; custom_vp->height = height; driver.video->viewport_info(driver.video_data, custom_vp); } if (driver.video->set_rotation) driver.video->set_rotation(driver.video_data, (g_settings.video.rotation + g_extern.system.rotation) % 4); #ifdef HAVE_X11 if (driver.display_type == RARCH_DISPLAY_X11) { RARCH_LOG("Suspending screensaver (X11).\n"); x11_suspend_screensaver(driver.video_window); } #endif // Video driver didn't provide an input driver so we use configured one. if (!driver.input) { RARCH_LOG("Graphics driver did not initialize an input driver. Attempting to pick a suitable driver.\n"); if (tmp) driver.input = tmp; else find_input_driver(); if (driver.input) { driver.input_data = driver.input->init(); if (!driver.input_data) { RARCH_ERR("Cannot initialize input driver. Exiting ...\n"); rarch_fail(1, "init_video_input()"); } } else { // This should never really happen as tmp (driver.input) is always found before this in find_driver_input(), // or we have aborted in a similar fashion anyways. rarch_fail(1, "init_video_input()"); } } #ifdef HAVE_OVERLAY rarch_main_command(RARCH_CMD_OVERLAY_DEINIT); rarch_main_command(RARCH_CMD_OVERLAY_INIT); #endif g_extern.measure_data.frame_time_samples_count = 0; }
void clim_draw_texture(const sf2d_texture *texture, int x, int y) { sf2d_draw_texture_part_scale(texture, x, y+texture->height, 0, next_pow2(texture->height) - texture->height, texture->width, texture->height, 1, -1); }
static bool d3d_init_chain(d3d_video_t *d3d, const video_info_t *video_info) { LPDIRECT3DDEVICE d3dr = (LPDIRECT3DDEVICE)d3d->dev; // Setup information for first pass. LinkInfo link_info = {0}; link_info.pass = &d3d->shader.pass[0]; link_info.tex_w = link_info.tex_h = video_info->input_scale * RARCH_SCALE_BASE; d3d_deinit_chain(d3d); d3d->chain = new renderchain_t(); if (!d3d->chain) return false; if (!renderchain_init(d3d->chain, &d3d->video_info, d3dr, d3d->cgCtx, &d3d->final_viewport, &link_info, d3d->video_info.rgb32 ? ARGB : 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; unsigned out_width = 0; unsigned out_height = 0; for (unsigned i = 1; i < d3d->shader.passes; i++) { renderchain_convert_geometry(d3d->chain, &link_info, out_width, out_height, current_width, current_height, &d3d->final_viewport); link_info.pass = &d3d->shader.pass[i]; link_info.tex_w = next_pow2(out_width); link_info.tex_h = next_pow2(out_height); current_width = out_width; current_height = out_height; if (!renderchain_add_pass(d3d->chain, &link_info)) { RARCH_ERR("[D3D9]: Failed to add pass.\n"); return false; } } if (!d3d_init_luts(d3d)) { RARCH_ERR("[D3D9]: Failed to init LUTs.\n"); return false; } #ifndef DONT_HAVE_STATE_TRACKER if (!d3d_init_imports(d3d)) { RARCH_ERR("[D3D9]: Failed to init imports.\n"); return false; } #endif return true; }
void rarch_init_filter(enum retro_pixel_format colfmt) { rarch_deinit_filter(); #ifdef HAVE_FILTERS_BUILTIN if (!g_settings.video.filter_idx) #else if (!*g_settings.video.filter_path) #endif return; // Deprecated format. Gets pre-converted. if (colfmt == RETRO_PIXEL_FORMAT_0RGB1555) colfmt = RETRO_PIXEL_FORMAT_RGB565; if (g_extern.system.hw_render_callback.context_type) { RARCH_WARN("Cannot use CPU filters when hardware rendering is used.\n"); return; } struct retro_game_geometry *geom = &g_extern.system.av_info.geometry; unsigned width = geom->max_width; unsigned height = geom->max_height; unsigned pow2_x = 0; unsigned pow2_y = 0; unsigned maxsize = 0; #ifdef HAVE_FILTERS_BUILTIN RARCH_LOG("Loading softfilter %d\n", g_settings.video.filter_idx); #else RARCH_LOG("Loading softfilter from \"%s\"\n", g_settings.video.filter_path); #endif g_extern.filter.filter = rarch_softfilter_new(g_settings.video.filter_path, RARCH_SOFTFILTER_THREADS_AUTO, colfmt, width, height); if (!g_extern.filter.filter) { #ifdef HAVE_FILTERS_BUILTIN RARCH_LOG("Loading softfilter %d\n", g_settings.video.filter_idx); #else RARCH_ERR("Failed to load filter \"%s\"\n", g_settings.video.filter_path); #endif return; } rarch_softfilter_get_max_output_size(g_extern.filter.filter, &width, &height); pow2_x = next_pow2(width); pow2_y = next_pow2(height); maxsize = max(pow2_x, pow2_y); g_extern.filter.scale = maxsize / RARCH_SCALE_BASE; g_extern.filter.out_rgb32 = rarch_softfilter_get_output_format(g_extern.filter.filter) == RETRO_PIXEL_FORMAT_XRGB8888; g_extern.filter.out_bpp = g_extern.filter.out_rgb32 ? sizeof(uint32_t) : sizeof(uint16_t); // TODO: Aligned output. g_extern.filter.buffer = malloc(width * height * g_extern.filter.out_bpp); if (!g_extern.filter.buffer) goto error; return; error: RARCH_ERR("Softfilter init failed.\n"); rarch_deinit_filter(); }
SDL_Window* set_video_mode(int w, int h, int flags) { static SDL_Window* wnd = NULL; static SDL_GLContext ctx = NULL; static int wnd_flags = 0; if(wnd) { SDL_DisplayMode mode; if(SDL_GetWindowDisplayMode(wnd, &mode) == 0) { mode.w = w; mode.h = h; if(SDL_SetWindowDisplayMode(wnd, &mode) == 0) { SDL_SetWindowSize(wnd, w, h); SDL_SetWindowFullscreen(wnd, flags&SDL_WINDOW_FULLSCREEN); SDL_SetWindowPosition(wnd, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); return wnd; } else { fprintf(stderr, "ERROR: Failed to set window display mode. Destroying window and creating a new one.\n"); } } else { fprintf(stderr, "ERROR: Failed to get window display mode. Destroying window and creating a new one.\n"); } } wnd_flags = flags; graphics::texture::unbuild_all(); #if defined(USE_SHADERS) SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); if(g_msaa > 0) { if(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1) != 0) { std::cerr << "MSAA(" << g_msaa << ") requested but mutlisample buffer couldn't be allocated." << std::endl; } else { size_t msaa = next_pow2(g_msaa); std::cerr << "Requesting MSAA of " << msaa; if(SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, msaa) != 0) { std::cerr << " -- Failure, disabled."; } std::cerr << std::endl; } } #endif if(global_renderer) { SDL_DestroyRenderer(global_renderer); global_renderer = NULL; } if(ctx) { SDL_GL_DeleteContext(ctx); ctx = NULL; } if(wnd) { SDL_DestroyWindow(wnd); global_main_window = wnd = NULL; } if(!(flags & CLEANUP_WINDOW_CONTEXT)) { global_main_window = wnd = SDL_CreateWindow(module::get_module_pretty_name().c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, w, h, flags); ctx = SDL_GL_CreateContext(wnd); global_renderer = SDL_CreateRenderer(wnd, -1, SDL_RENDERER_ACCELERATED); #if defined(__GLEW_H__) GLenum glew_status = glewInit(); ASSERT_EQ(glew_status, GLEW_OK); #endif reset_opengl_state(); graphics::texture::rebuild_all(); texture_frame_buffer::rebuild(); } #if defined(USE_SHADERS) int depth_size, stencil_size; SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depth_size); SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &stencil_size); std::cerr << "Depth buffer size: " << depth_size << std::endl; std::cerr << "Stenicl buffer size: " << stencil_size << std::endl; int depth; glGetIntegerv(GL_DEPTH_BITS, &depth); std::cerr << "Depth(from GL) buffer size: " << depth << std::endl; if(g_msaa > 0 && SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &g_msaa_set) == 0) { std::cerr << "Actual MSAA: " << g_msaa_set << std::endl; } #endif return wnd; }
static void *sl_init(const char *device, unsigned rate, unsigned latency) { (void)device; SLDataFormat_PCM fmt_pcm = {0}; SLDataSource audio_src = {0}; SLDataSink audio_sink = {0}; SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {0}; SLDataLocator_OutputMix loc_outmix = {0}; SLInterfaceID id = SL_IID_ANDROIDSIMPLEBUFFERQUEUE; SLboolean req = SL_BOOLEAN_TRUE; SLresult res = 0; sl_t *sl = (sl_t*)calloc(1, sizeof(sl_t)); if (!sl) goto error; RARCH_LOG("[SLES]: Requested audio latency: %d ms.", latency); GOTO_IF_FAIL(slCreateEngine(&sl->engine_object, 0, NULL, 0, NULL, NULL)); GOTO_IF_FAIL(SLObjectItf_Realize(sl->engine_object, SL_BOOLEAN_FALSE)); GOTO_IF_FAIL(SLObjectItf_GetInterface(sl->engine_object, SL_IID_ENGINE, &sl->engine)); GOTO_IF_FAIL(SLEngineItf_CreateOutputMix(sl->engine, &sl->output_mix, 0, NULL, NULL)); GOTO_IF_FAIL(SLObjectItf_Realize(sl->output_mix, SL_BOOLEAN_FALSE)); if (g_settings.audio.block_frames) sl->buf_size = g_settings.audio.block_frames * 4; else sl->buf_size = next_pow2(32 * latency); sl->buf_count = (latency * 4 * out_rate + 500) / 1000; sl->buf_count = (sl->buf_count + sl->buf_size / 2) / sl->buf_size; sl->buffer = (uint8_t**)calloc(sizeof(uint8_t*), sl->buf_count); if (!sl->buffer) goto error; sl->buffer_chunk = (uint8_t*)calloc(sl->buf_count, sl->buf_size); if (!sl->buffer_chunk) goto error; for (unsigned i = 0; i < sl->buf_count; i++) sl->buffer[i] = sl->buffer_chunk + i * sl->buf_size; RARCH_LOG("[SLES]: Setting audio latency: Block size = %u, Blocks = %u, Total = %u ...\n", sl->buf_size, sl->buf_count, sl->buf_size * sl->buf_count); fmt_pcm.formatType = SL_DATAFORMAT_PCM; fmt_pcm.numChannels = 2; fmt_pcm.samplesPerSec = rate * 1000; // Samplerate is in milli-Hz. fmt_pcm.bitsPerSample = 16; fmt_pcm.containerSize = 16; fmt_pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; fmt_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN; // Android only. audio_src.pLocator = &loc_bufq; audio_src.pFormat = &fmt_pcm; loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; loc_bufq.numBuffers = sl->buf_count; loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; loc_outmix.outputMix = sl->output_mix; audio_sink.pLocator = &loc_outmix; GOTO_IF_FAIL(SLEngineItf_CreateAudioPlayer(sl->engine, &sl->buffer_queue_object, &audio_src, &audio_sink, 1, &id, &req)); GOTO_IF_FAIL(SLObjectItf_Realize(sl->buffer_queue_object, SL_BOOLEAN_FALSE)); GOTO_IF_FAIL(SLObjectItf_GetInterface(sl->buffer_queue_object, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &sl->buffer_queue)); sl->cond = scond_new(); sl->lock = slock_new(); (*sl->buffer_queue)->RegisterCallback(sl->buffer_queue, opensl_callback, sl); // Enqueue a bit to get stuff rolling. sl->buffered_blocks = sl->buf_count; sl->buffer_index = 0; for (unsigned i = 0; i < sl->buf_count; i++) (*sl->buffer_queue)->Enqueue(sl->buffer_queue, sl->buffer[i], sl->buf_size); GOTO_IF_FAIL(SLObjectItf_GetInterface(sl->buffer_queue_object, SL_IID_PLAY, &sl->player)); GOTO_IF_FAIL(SLPlayItf_SetPlayState(sl->player, SL_PLAYSTATE_PLAYING)); return sl; error: RARCH_ERR("Couldn't initialize OpenSL ES driver, error code: [%d].\n", (int)res); sl_free(sl); return NULL; }
void gl_init_fbo(gl_t *gl, unsigned width, unsigned height) { // No need to use FBOs. if (!g_settings.video.render_to_texture && gl_shader_num() == 0) return; struct gl_fbo_scale scale, scale_last; gl_shader_scale(1, &scale); gl_shader_scale(gl_shader_num(), &scale_last); // No need to use FBOs. if (gl_shader_num() == 1 && !scale.valid && !g_settings.video.render_to_texture) return; if (!load_fbo_proc()) { RARCH_ERR("Failed to locate FBO functions. Won't be able to use render-to-texture.\n"); return; } gl->fbo_pass = gl_shader_num() - 1; if (scale_last.valid) gl->fbo_pass++; if (gl->fbo_pass <= 0) gl->fbo_pass = 1; if (!scale.valid) { scale.scale_x = g_settings.video.fbo_scale_x; scale.scale_y = g_settings.video.fbo_scale_y; scale.type_x = scale.type_y = RARCH_SCALE_INPUT; scale.valid = true; } gl->fbo_scale[0] = scale; for (int i = 1; i < gl->fbo_pass; i++) { gl_shader_scale(i + 1, &gl->fbo_scale[i]); if (!gl->fbo_scale[i].valid) { gl->fbo_scale[i].scale_x = gl->fbo_scale[i].scale_y = 1.0f; gl->fbo_scale[i].type_x = gl->fbo_scale[i].type_y = RARCH_SCALE_INPUT; gl->fbo_scale[i].valid = true; } } gl_compute_fbo_geometry(gl, width, height, gl->win_width, gl->win_height); for (int i = 0; i < gl->fbo_pass; i++) { gl->fbo_rect[i].width = next_pow2(gl->fbo_rect[i].img_width); gl->fbo_rect[i].height = next_pow2(gl->fbo_rect[i].img_height); RARCH_LOG("Creating FBO %d @ %ux%u\n", i, gl->fbo_rect[i].width, gl->fbo_rect[i].height); } gl_create_fbo_textures(gl); if (!gl_create_fbo_targets(gl)) { glDeleteTextures(gl->fbo_pass, gl->fbo_texture); return; } gl->fbo_inited = true; }
inline uint32_t required_capacity(uint32_t max_count) { return next_pow2((uint32_t)ceilf(max_count / load_factor)); }
/* Set up render to texture. */ void gl2_renderchain_init( gl_t *gl, void *chain_data, unsigned fbo_width, unsigned fbo_height) { int i; unsigned width, height; video_shader_ctx_scale_t scaler; video_shader_ctx_info_t shader_info; struct gfx_fbo_scale scale, scale_last; gl2_renderchain_t *chain = (gl2_renderchain_t*)chain_data; if (!video_shader_driver_info(&shader_info)) return; if (!gl || shader_info.num == 0) return; video_driver_get_size(&width, &height); scaler.idx = 1; scaler.scale = &scale; video_shader_driver_scale(&scaler); scaler.idx = shader_info.num; scaler.scale = &scale_last; video_shader_driver_scale(&scaler); /* we always want FBO to be at least initialized on startup for consoles */ if (shader_info.num == 1 && !scale.valid) return; if (!gl->has_fbo) { RARCH_ERR("[GL]: Failed to locate FBO functions. Won't be able to use render-to-texture.\n"); return; } chain->fbo_pass = shader_info.num - 1; if (scale_last.valid) chain->fbo_pass++; if (!scale.valid) { scale.scale_x = 1.0f; scale.scale_y = 1.0f; scale.type_x = scale.type_y = RARCH_SCALE_INPUT; scale.valid = true; } chain->fbo_scale[0] = scale; for (i = 1; i < chain->fbo_pass; i++) { scaler.idx = i + 1; scaler.scale = &chain->fbo_scale[i]; video_shader_driver_scale(&scaler); if (!chain->fbo_scale[i].valid) { chain->fbo_scale[i].scale_x = chain->fbo_scale[i].scale_y = 1.0f; chain->fbo_scale[i].type_x = chain->fbo_scale[i].type_y = RARCH_SCALE_INPUT; chain->fbo_scale[i].valid = true; } } gl2_renderchain_recompute_pass_sizes(gl, chain_data, fbo_width, fbo_height, width, height); for (i = 0; i < chain->fbo_pass; i++) { gl->fbo_rect[i].width = next_pow2(gl->fbo_rect[i].img_width); gl->fbo_rect[i].height = next_pow2(gl->fbo_rect[i].img_height); RARCH_LOG("[GL]: Creating FBO %d @ %ux%u\n", i, gl->fbo_rect[i].width, gl->fbo_rect[i].height); } gl->fbo_feedback_enable = video_shader_driver_get_feedback_pass( &gl->fbo_feedback_pass); if (gl->fbo_feedback_enable && gl->fbo_feedback_pass < (unsigned)chain->fbo_pass) { RARCH_LOG("[GL]: Creating feedback FBO %d @ %ux%u\n", i, gl->fbo_rect[gl->fbo_feedback_pass].width, gl->fbo_rect[gl->fbo_feedback_pass].height); } else if (gl->fbo_feedback_enable) { RARCH_WARN("[GL]: Tried to create feedback FBO of pass #%u, but there are only %d FBO passes. Will use input texture as feedback texture.\n", gl->fbo_feedback_pass, chain->fbo_pass); gl->fbo_feedback_enable = false; } gl_create_fbo_textures(gl, chain); if (!gl || !gl_create_fbo_targets(gl, chain)) { glDeleteTextures(chain->fbo_pass, chain->fbo_texture); RARCH_ERR("[GL]: Failed to create FBO targets. Will continue without FBO.\n"); return; } gl->fbo_inited = true; }
static void *gl_raster_font_init_font(void *data, const char *font_path, float font_size) { unsigned width, height; uint8_t *tmp_buffer; const struct font_atlas *atlas = NULL; gl_raster_t *font = (gl_raster_t*)calloc(1, sizeof(*font)); if (!font) return NULL; font->gl = (gl_t*)data; if (!font_renderer_create_default(&font->font_driver, &font->font_data, font_path, font_size)) { RARCH_WARN("Couldn't init font renderer.\n"); free(font); return NULL; } glGenTextures(1, &font->tex); glBindTexture(GL_TEXTURE_2D, font->tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); atlas = font->font_driver->get_atlas(font->font_data); width = next_pow2(atlas->width); height = next_pow2(atlas->height); /* Ideally, we'd use single component textures, but the * difference in ways to do that between core GL and GLES/legacy GL * is too great to bother going down that route. */ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); tmp_buffer = (uint8_t*)malloc(atlas->width * atlas->height * 4); if (tmp_buffer) { unsigned i; uint8_t *dst = tmp_buffer; const uint8_t *src = atlas->buffer; for (i = 0; i < atlas->width * atlas->height; i++) { *dst++ = 0xff; *dst++ = 0xff; *dst++ = 0xff; *dst++ = *src++; } glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, atlas->width, atlas->height, GL_RGBA, GL_UNSIGNED_BYTE, tmp_buffer); free(tmp_buffer); } font->tex_width = width; font->tex_height = height; glBindTexture(GL_TEXTURE_2D, font->gl->texture[font->gl->tex_index]); return font; }
//-------------------------------------------------------------------------- bool init( uword fixed_size_entries, uword fixed_size_entry_size, bool use_heap ) { assert ((fixed_size_entries && fixed_size_entry_size) || use_heap); assert (!m_fixed_begin && !m_use_heap); uword entries = 0; uword entry_sz = 0; uword bsz = 0; if (fixed_size_entries && fixed_size_entry_size) { entries = next_pow2 (fixed_size_entries); entry_sz = next_pow2 (fixed_size_entry_size); if ((entries < fixed_size_entries) || (entry_sz < fixed_size_entry_size) ) { return false; } bsz = entries * entry_sz; if ((bsz / entries) != entry_sz) { return false; } } if (!bsz) { zero(); m_use_heap = use_heap; return true; } m_fixed_begin = (u8*) ::operator new (bsz, std::nothrow); if (!m_fixed_begin) { return false; } try { m_list.construct (entries); } catch (...) { free(); return false; } m_fixed_end = m_fixed_begin + bsz; m_entry_size = entry_sz; m_use_heap = use_heap; for (uword i = 0; i < entries; ++i) { m_list->bounded_push (m_fixed_begin + (i * m_entry_size)); } return true; }
/** * recording_init: * * Initializes recording. * * Returns: true (1) if successful, otherwise false (0). **/ bool recording_init(void) { char recording_file[PATH_MAX_LENGTH] = {0}; struct ffemu_params params = {0}; global_t *global = global_get_ptr(); driver_t *driver = driver_get_ptr(); settings_t *settings = config_get_ptr(); struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); const struct retro_hw_render_callback *hw_render = (const struct retro_hw_render_callback*)video_driver_callback(); bool *recording_enabled = recording_is_enabled(); if (!*recording_enabled) return false; if (global->inited.core.type == CORE_TYPE_DUMMY) { RARCH_WARN("%s\n", msg_hash_to_str(MSG_USING_LIBRETRO_DUMMY_CORE_RECORDING_SKIPPED)); return false; } if (!settings->video.gpu_record && hw_render->context_type) { RARCH_WARN("%s.\n", msg_hash_to_str(MSG_HW_RENDERED_MUST_USE_POSTSHADED_RECORDING)); return false; } RARCH_LOG("%s: FPS: %.4f, Sample rate: %.4f\n", msg_hash_to_str(MSG_CUSTOM_TIMING_GIVEN), (float)av_info->timing.fps, (float)av_info->timing.sample_rate); strlcpy(recording_file, global->record.path, sizeof(recording_file)); if (global->record.use_output_dir) fill_pathname_join(recording_file, global->record.output_dir, global->record.path, sizeof(recording_file)); params.out_width = av_info->geometry.base_width; params.out_height = av_info->geometry.base_height; params.fb_width = av_info->geometry.max_width; params.fb_height = av_info->geometry.max_height; params.channels = 2; params.filename = recording_file; params.fps = av_info->timing.fps; params.samplerate = av_info->timing.sample_rate; params.pix_fmt = (video_driver_get_pixel_format() == RETRO_PIXEL_FORMAT_XRGB8888) ? FFEMU_PIX_ARGB8888 : FFEMU_PIX_RGB565; params.config = NULL; if (*global->record.config) params.config = global->record.config; if (settings->video.gpu_record && driver->video->read_viewport) { struct video_viewport vp = {0}; video_driver_viewport_info(&vp); if (!vp.width || !vp.height) { RARCH_ERR("Failed to get viewport information from video driver. " "Cannot start recording ...\n"); return false; } params.out_width = vp.width; params.out_height = vp.height; params.fb_width = next_pow2(vp.width); params.fb_height = next_pow2(vp.height); if (settings->video.force_aspect && (video_driver_get_aspect_ratio() > 0.0f)) params.aspect_ratio = video_driver_get_aspect_ratio(); else params.aspect_ratio = (float)vp.width / vp.height; params.pix_fmt = FFEMU_PIX_BGR24; global->record.gpu_width = vp.width; global->record.gpu_height = vp.height; RARCH_LOG("%s %u x %u\n", msg_hash_to_str(MSG_DETECTED_VIEWPORT_OF), vp.width, vp.height); global->record.gpu_buffer = (uint8_t*)malloc(vp.width * vp.height * 3); if (!global->record.gpu_buffer) return false; } else { if (global->record.width || global->record.height) { params.out_width = global->record.width; params.out_height = global->record.height; } if (settings->video.force_aspect && (video_driver_get_aspect_ratio() > 0.0f)) params.aspect_ratio = video_driver_get_aspect_ratio(); else params.aspect_ratio = (float)params.out_width / params.out_height; if (settings->video.post_filter_record && video_driver_frame_filter_alive()) { unsigned max_width = 0; unsigned max_height = 0; if (video_driver_frame_filter_is_32bit()) params.pix_fmt = FFEMU_PIX_ARGB8888; else params.pix_fmt = FFEMU_PIX_RGB565; rarch_softfilter_get_max_output_size( video_driver_frame_filter_get_ptr(), &max_width, &max_height); params.fb_width = next_pow2(max_width); params.fb_height = next_pow2(max_height); } } RARCH_LOG("%s %s @ %ux%u. (FB size: %ux%u pix_fmt: %u)\n", msg_hash_to_str(MSG_RECORDING_TO), global->record.path, params.out_width, params.out_height, params.fb_width, params.fb_height, (unsigned)params.pix_fmt); if (!record_driver_init_first(&driver->recording, &driver->recording_data, ¶ms)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_START_RECORDING)); event_command(EVENT_CMD_GPU_RECORD_DEINIT); return false; } return true; }
static void init_video_filter(enum retro_pixel_format colfmt) { unsigned width, height, pow2_x, pow2_y, maxsize; struct retro_game_geometry *geom = NULL; settings_t *settings = config_get_ptr(); struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); deinit_video_filter(); if (!*settings->video.softfilter_plugin) return; /* Deprecated format. Gets pre-converted. */ if (colfmt == RETRO_PIXEL_FORMAT_0RGB1555) colfmt = RETRO_PIXEL_FORMAT_RGB565; if (video_state.hw_render_callback.context_type) { RARCH_WARN("Cannot use CPU filters when hardware rendering is used.\n"); return; } geom = av_info ? (struct retro_game_geometry*)&av_info->geometry : NULL; width = geom->max_width; height = geom->max_height; video_state.filter.filter = rarch_softfilter_new( settings->video.softfilter_plugin, RARCH_SOFTFILTER_THREADS_AUTO, colfmt, width, height); if (!video_state.filter.filter) { RARCH_ERR("Failed to load filter.\n"); return; } rarch_softfilter_get_max_output_size(video_state.filter.filter, &width, &height); pow2_x = next_pow2(width); pow2_y = next_pow2(height); maxsize = max(pow2_x, pow2_y); video_state.filter.scale = maxsize / RARCH_SCALE_BASE; video_state.filter.out_rgb32 = rarch_softfilter_get_output_format( video_state.filter.filter) == RETRO_PIXEL_FORMAT_XRGB8888; video_state.filter.out_bpp = video_state.filter.out_rgb32 ? sizeof(uint32_t) : sizeof(uint16_t); /* TODO: Aligned output. */ video_state.filter.buffer = malloc(width * height * video_state.filter.out_bpp); if (!video_state.filter.buffer) goto error; return; error: RARCH_ERR("Softfilter initialization failed.\n"); deinit_video_filter(); }
static void *alsa_qsa_init(const char *device, unsigned rate, unsigned latency) { int err, card, dev, i; snd_pcm_channel_params_t params = {0}; snd_pcm_channel_info_t pi; snd_pcm_channel_setup_t setup = {0}; settings_t *settings = config_get_ptr(); alsa_t *alsa = (alsa_t*)calloc(1, sizeof(alsa_t)); if (!alsa) return NULL; (void)device; (void)rate; (void)latency; if ((err = snd_pcm_open_preferred(&alsa->pcm, &card, &dev, SND_PCM_OPEN_PLAYBACK)) < 0) { RARCH_ERR("[ALSA QSA]: Audio open error: %s\n", snd_strerror(err)); goto error; } if((err = snd_pcm_nonblock_mode(alsa->pcm, 1)) < 0) { RARCH_ERR("[ALSA QSA]: Can't set blocking mode: %s\n", snd_strerror(err)); goto error; } memset(&pi, 0, sizeof(pi)); pi.channel = SND_PCM_CHANNEL_PLAYBACK; if ((err = snd_pcm_channel_info(alsa->pcm, &pi)) < 0) { RARCH_ERR("[ALSA QSA]: snd_pcm_channel_info failed: %s\n", snd_strerror(err)); goto error; } memset(¶ms, 0, sizeof(params)); params.channel = SND_PCM_CHANNEL_PLAYBACK; params.mode = SND_PCM_MODE_BLOCK; params.format.interleave = 1; params.format.format = SND_PCM_SFMT_S16_LE; params.format.rate = DEFAULT_RATE; params.format.voices = 2; params.start_mode = SND_PCM_START_FULL; params.stop_mode = SND_PCM_STOP_STOP; params.buf.block.frag_size = pi.max_fragment_size; params.buf.block.frags_min = 2; params.buf.block.frags_max = 8; RARCH_LOG("Fragment size: %d\n", params.buf.block.frag_size); RARCH_LOG("Min Fragment size: %d\n", params.buf.block.frags_min); RARCH_LOG("Max Fragment size: %d\n", params.buf.block.frags_max); if ((err = snd_pcm_channel_params(alsa->pcm, ¶ms)) < 0) { RARCH_ERR("[ALSA QSA]: Channel Parameter Error: %s\n", snd_strerror(err)); goto error; } setup.channel = SND_PCM_CHANNEL_PLAYBACK; if ((err = snd_pcm_channel_setup(alsa->pcm, &setup)) < 0) { RARCH_ERR("[ALSA QSA]: Channel Parameter Read Back Error: %s\n", snd_strerror(err)); goto error; } if (settings->audio.block_frames) alsa->buf_size = settings->audio.block_frames * 4; else alsa->buf_size = next_pow2(32 * latency); RARCH_LOG("[ALSA QSA]: buffer size: %u bytes\n", alsa->buf_size); alsa->buf_count = (latency * 4 * rate + 500) / 1000; alsa->buf_count = (alsa->buf_count + alsa->buf_size / 2) / alsa->buf_size; if ((err = snd_pcm_channel_prepare(alsa->pcm, SND_PCM_CHANNEL_PLAYBACK)) < 0) { RARCH_ERR("[ALSA QSA]: Channel Prepare Error: %s\n", snd_strerror(err)); goto error; } alsa->buffer = (uint8_t**)calloc(sizeof(uint8_t*), alsa->buf_count); if (!alsa->buffer) goto error; alsa->buffer_chunk = (uint8_t*)calloc(alsa->buf_count, alsa->buf_size); if (!alsa->buffer_chunk) goto error; for (i = 0; i < alsa->buf_count; i++) alsa->buffer[i] = alsa->buffer_chunk + i * alsa->buf_size; alsa->has_float = false; alsa->can_pause = true; RARCH_LOG("[ALSA QSA]: Can pause: %s.\n", alsa->can_pause ? "yes" : "no"); return alsa; error: return (void*)-1; }
void init_video(void) { unsigned max_dim, scale, width, height; video_viewport_t *custom_vp = NULL; const input_driver_t *tmp = NULL; const struct retro_game_geometry *geom = NULL; video_info_t video = {0}; static uint16_t dummy_pixels[32] = {0}; driver_t *driver = driver_get_ptr(); settings_t *settings = config_get_ptr(); rarch_system_info_t *system = rarch_system_info_get_ptr(); struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); init_video_filter(video_state.pix_fmt); event_command(EVENT_CMD_SHADER_DIR_INIT); if (av_info) geom = (const struct retro_game_geometry*)&av_info->geometry; max_dim = max(geom->max_width, geom->max_height); scale = next_pow2(max_dim) / RARCH_SCALE_BASE; scale = max(scale, 1); if (video_state.filter.filter) scale = video_state.filter.scale; /* Update core-dependent aspect ratio values. */ video_viewport_set_square_pixel(geom->base_width, geom->base_height); video_viewport_set_core(); video_viewport_set_config(); /* Update CUSTOM viewport. */ custom_vp = video_viewport_get_custom(); if (settings->video.aspect_ratio_idx == ASPECT_RATIO_CUSTOM) { float default_aspect = aspectratio_lut[ASPECT_RATIO_CORE].value; aspectratio_lut[ASPECT_RATIO_CUSTOM].value = (custom_vp->width && custom_vp->height) ? (float)custom_vp->width / custom_vp->height : default_aspect; } video_driver_set_aspect_ratio_value( aspectratio_lut[settings->video.aspect_ratio_idx].value); if (settings->video.fullscreen) { width = settings->video.fullscreen_x; height = settings->video.fullscreen_y; } else { if (settings->video.force_aspect) { /* Do rounding here to simplify integer scale correctness. */ unsigned base_width = roundf(geom->base_height * video_driver_get_aspect_ratio()); width = roundf(base_width * settings->video.scale); } else width = roundf(geom->base_width * settings->video.scale); height = roundf(geom->base_height * settings->video.scale); } if (width && height) RARCH_LOG("Video @ %ux%u\n", width, height); else RARCH_LOG("Video @ fullscreen\n"); driver->display_type = RARCH_DISPLAY_NONE; driver->video_display = 0; driver->video_window = 0; if (!init_video_pixel_converter(RARCH_SCALE_BASE * scale)) { RARCH_ERR("Failed to initialize pixel converter.\n"); rarch_fail(1, "init_video()"); } video.width = width; video.height = height; video.fullscreen = settings->video.fullscreen; video.vsync = settings->video.vsync && !system->force_nonblock; video.force_aspect = settings->video.force_aspect; #ifdef GEKKO video.viwidth = settings->video.viwidth; video.vfilter = settings->video.vfilter; #endif video.smooth = settings->video.smooth; video.input_scale = scale; video.rgb32 = video_state.filter.filter ? video_state.filter.out_rgb32 : (video_state.pix_fmt == RETRO_PIXEL_FORMAT_XRGB8888); tmp = (const input_driver_t*)driver->input; /* Need to grab the "real" video driver interface on a reinit. */ find_video_driver(); #ifdef HAVE_THREADS if (settings->video.threaded && !video_state.hw_render_callback.context_type) { /* Can't do hardware rendering with threaded driver currently. */ RARCH_LOG("Starting threaded video driver ...\n"); if (!rarch_threaded_video_init(&driver->video, &driver->video_data, &driver->input, &driver->input_data, driver->video, &video)) { RARCH_ERR("Cannot open threaded video driver ... Exiting ...\n"); rarch_fail(1, "init_video()"); } } else #endif driver->video_data = driver->video->init(&video, &driver->input, &driver->input_data); if (!driver->video_data) { RARCH_ERR("Cannot open video driver ... Exiting ...\n"); rarch_fail(1, "init_video()"); } driver->video_poke = NULL; if (driver->video->poke_interface) driver->video->poke_interface(driver->video_data, &driver->video_poke); if (driver->video->viewport_info && (!custom_vp->width || !custom_vp->height)) { /* Force custom viewport to have sane parameters. */ custom_vp->width = width; custom_vp->height = height; video_driver_viewport_info(custom_vp); } video_driver_set_rotation( (settings->video.rotation + system->rotation) % 4); video_driver_suppress_screensaver(settings->ui.suspend_screensaver_enable); if (!driver->input) init_video_input(tmp); event_command(EVENT_CMD_OVERLAY_DEINIT); event_command(EVENT_CMD_OVERLAY_INIT); video_driver_cached_frame_set(&dummy_pixels, 4, 4, 8); #if defined(PSP) video_driver_set_texture_frame(&dummy_pixels, false, 1, 1, 1.0f); #endif }
bool d3d_init_chain(void *data, const video_info_t *video_info) { D3DVideo *d3d = reinterpret_cast<D3DVideo*>(data); // Setup information for first pass. LinkInfo link_info = {0}; link_info.pass = &d3d->shader.pass[0]; link_info.tex_w = link_info.tex_h = video_info->input_scale * RARCH_SCALE_BASE; delete d3d->chain; d3d->chain = new RenderChain( &d3d->video_info, d3d->dev, d3d->cgCtx, d3d->final_viewport); if (!d3d->chain->init(link_info, d3d->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; unsigned out_width = 0; unsigned out_height = 0; for (unsigned i = 1; i < d3d->shader.passes; i++) { RenderChain::convert_geometry(link_info, out_width, out_height, current_width, current_height, d3d->final_viewport); link_info.pass = &d3d->shader.pass[i]; link_info.tex_w = next_pow2(out_width); link_info.tex_h = next_pow2(out_height); current_width = out_width; current_height = out_height; if (!d3d->chain->add_pass(link_info)) { RARCH_ERR("[D3D9]: Failed to add pass.\n"); return false; } } if (!d3d_init_luts(d3d)) { RARCH_ERR("[D3D9]: Failed to init LUTs.\n"); return false; } if (!d3d_init_imports(d3d)) { RARCH_ERR("[D3D9]: Failed to init imports.\n"); return false; } return true; }
static bool is_pow2(int x) { return x == next_pow2(x); }