diff --git a/include/PICA/pica_frag_config.hpp b/include/PICA/pica_frag_config.hpp index a253f4d6..114b76f1 100644 --- a/include/PICA/pica_frag_config.hpp +++ b/include/PICA/pica_frag_config.hpp @@ -115,7 +115,7 @@ namespace PICA { bumpSelector = Helpers::getBits<22, 2>(config0); clampHighlights = Helpers::getBit<27>(config0); bumpMode = Helpers::getBits<28, 2>(config0); - bumpRenorm = Helpers::getBit<30>(config0) ^ 1; // 0 = enable so flip it with xor + bumpRenorm = Helpers::getBit<30>(config0) ^ 1; // 0 = enable so flip it with xor for (int i = 0; i < totalLightCount; i++) { auto& light = lights[i]; @@ -220,7 +220,10 @@ namespace PICA { } // If this fails you probably added a new field to the struct and forgot to update the copy constructor - static_assert(sizeof(FragmentConfig) == sizeof(outConfig.raw) + sizeof(texConfig) + sizeof(fogConfig.raw) + sizeof(lighting.raw) + 7 * sizeof(LightingLUTConfig) + 8 * sizeof(Light)); + static_assert( + sizeof(FragmentConfig) == sizeof(outConfig.raw) + sizeof(texConfig) + sizeof(fogConfig.raw) + sizeof(lighting.raw) + + 7 * sizeof(LightingLUTConfig) + 8 * sizeof(Light) + ); return *this; } diff --git a/include/emulator.hpp b/include/emulator.hpp index ffbebd9c..6e60d9fa 100644 --- a/include/emulator.hpp +++ b/include/emulator.hpp @@ -55,7 +55,7 @@ class Emulator { static constexpr u32 width = 400; static constexpr u32 height = 240 * 2; // * 2 because 2 screens ROMType romType = ROMType::None; - bool running = false; // Is the emulator running a game? + bool running = false; // Is the emulator running a game? private: #ifdef PANDA3DS_ENABLE_HTTP_SERVER diff --git a/include/renderer.hpp b/include/renderer.hpp index 0503d2ab..3b6606bf 100644 --- a/include/renderer.hpp +++ b/include/renderer.hpp @@ -1,8 +1,8 @@ #pragma once #include +#include #include #include -#include #include "PICA/pica_vertex.hpp" #include "PICA/regs.hpp" diff --git a/include/renderer_gl/async_compiler.hpp b/include/renderer_gl/async_compiler.hpp index b2c11c40..b57ce972 100644 --- a/include/renderer_gl/async_compiler.hpp +++ b/include/renderer_gl/async_compiler.hpp @@ -3,52 +3,48 @@ #include #include -#include "opengl.hpp" -#include "renderer_gl/renderer_gl.hpp" #include "PICA/pica_frag_config.hpp" #include "lockfree/spsc/queue.hpp" +#include "opengl.hpp" +#include "renderer_gl/renderer_gl.hpp" -namespace PICA::ShaderGen -{ - class FragmentGenerator; +namespace PICA::ShaderGen { + class FragmentGenerator; } -namespace AsyncCompiler -{ - void* createContext(void* userdata); - void makeCurrent(void* userdata, void* context); - void destroyContext(void* context); -} +namespace AsyncCompiler { + void* createContext(void* userdata); + void makeCurrent(void* userdata, void* context); + void destroyContext(void* context); +} // namespace AsyncCompiler -struct CompilingProgram -{ - CachedProgram* program; - PICA::FragmentConfig* config; +struct CompilingProgram { + CachedProgram* program; + PICA::FragmentConfig* config; }; -struct AsyncCompilerThread -{ - explicit AsyncCompilerThread(PICA::ShaderGen::FragmentGenerator& fragShaderGen, void* userdata); - ~AsyncCompilerThread(); +struct AsyncCompilerThread { + explicit AsyncCompilerThread(PICA::ShaderGen::FragmentGenerator& fragShaderGen, void* userdata); + ~AsyncCompilerThread(); - // Called from the emulator thread to queue a fragment configuration for compilation - // Returns false if the queue is full, true otherwise - void PushFragmentConfig(const PICA::FragmentConfig& config, CachedProgram* cachedProgram); + // Called from the emulator thread to queue a fragment configuration for compilation + // Returns false if the queue is full, true otherwise + void PushFragmentConfig(const PICA::FragmentConfig& config, CachedProgram* cachedProgram); - // Wait for all queued fragment configurations to be compiled - void Finish(); + // Wait for all queued fragment configurations to be compiled + void Finish(); -private: - PICA::ShaderGen::FragmentGenerator& fragShaderGen; - OpenGL::Shader defaultShadergenVs; + private: + PICA::ShaderGen::FragmentGenerator& fragShaderGen; + OpenGL::Shader defaultShadergenVs; - // Our lockfree queue only allows for trivial types, so we preallocate enough structs - // to avoid dynamic allocation on each push - int preallocatedProgramsIndex; - static constexpr int preallocatedProgramsSize = 256; - std::array preallocatedPrograms; - lockfree::spsc::Queue programQueue; - std::atomic_bool running; - std::atomic_flag hasWork = ATOMIC_FLAG_INIT; - std::thread thread; + // Our lockfree queue only allows for trivial types, so we preallocate enough structs + // to avoid dynamic allocation on each push + int preallocatedProgramsIndex; + static constexpr int preallocatedProgramsSize = 256; + std::array preallocatedPrograms; + lockfree::spsc::Queue programQueue; + std::atomic_bool running; + std::atomic_flag hasWork = ATOMIC_FLAG_INIT; + std::thread thread; }; \ No newline at end of file diff --git a/include/renderer_gl/renderer_gl.hpp b/include/renderer_gl/renderer_gl.hpp index 8f2cd96d..819e00f5 100644 --- a/include/renderer_gl/renderer_gl.hpp +++ b/include/renderer_gl/renderer_gl.hpp @@ -6,13 +6,13 @@ #include #include -#include "config.hpp" #include "PICA/float_types.hpp" #include "PICA/pica_frag_config.hpp" #include "PICA/pica_hash.hpp" #include "PICA/pica_vertex.hpp" #include "PICA/regs.hpp" #include "PICA/shader_gen.hpp" +#include "config.hpp" #include "gl_state.hpp" #include "helpers.hpp" #include "logger.hpp" @@ -42,7 +42,7 @@ class RendererGL final : public Renderer { OpenGL::VertexBuffer vbo; ShaderMode shaderMode = EmulatorConfig::defaultShaderMode; - // Data + // Data struct { // TEV configuration uniform locations GLint textureEnvSourceLoc = -1; @@ -112,7 +112,7 @@ class RendererGL final : public Renderer { void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) override; // Clear a GPU buffer in VRAM void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) override; // Perform display transfer void textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32 inputSize, u32 outputSize, u32 flags) override; - void drawVertices(PICA::PrimType primType, std::span vertices) override; // Draw the given vertices + void drawVertices(PICA::PrimType primType, std::span vertices) override; // Draw the given vertices void deinitGraphicsContext() override; virtual bool supportsShaderReload() override { return true; } @@ -120,7 +120,7 @@ class RendererGL final : public Renderer { virtual void setUbershader(const std::string& shader) override; virtual void setShaderMode(ShaderMode mode) override { shaderMode = mode; } - + std::optional getColourBuffer(u32 addr, PICA::ColorFmt format, u32 width, u32 height, bool createIfnotFound = true); // Note: The caller is responsible for deleting the currently bound FBO before calling this diff --git a/src/config.cpp b/src/config.cpp index 1480054d..9cf6ef67 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -136,7 +136,7 @@ void EmulatorConfig::save() { data["General"]["EnableDiscordRPC"] = discordRpcEnabled; data["General"]["UsePortableBuild"] = usePortableBuild; data["General"]["DefaultRomPath"] = defaultRomPath.string(); - + data["GPU"]["EnableShaderJIT"] = shaderJitEnabled; data["GPU"]["Renderer"] = std::string(Renderer::typeToString(rendererType)); data["GPU"]["EnableVSync"] = vsyncEnabled; diff --git a/src/core/PICA/gpu.cpp b/src/core/PICA/gpu.cpp index ce97d8fa..7d486b28 100644 --- a/src/core/PICA/gpu.cpp +++ b/src/core/PICA/gpu.cpp @@ -365,7 +365,7 @@ PICA::Vertex GPU::getImmediateModeVertex() { // Run VS and return vertex data. TODO: Don't hardcode offsets for each attribute shaderUnit.vs.run(); - + // Map shader outputs to fixed function properties const u32 totalShaderOutputs = regs[PICA::InternalRegs::ShaderOutputCount] & 7; for (int i = 0; i < totalShaderOutputs; i++) { diff --git a/src/core/renderer_gl/async_compiler.cpp b/src/core/renderer_gl/async_compiler.cpp index 23c33d68..95631c78 100644 --- a/src/core/renderer_gl/async_compiler.cpp +++ b/src/core/renderer_gl/async_compiler.cpp @@ -1,83 +1,75 @@ #include "renderer_gl/async_compiler.hpp" -AsyncCompilerThread::AsyncCompilerThread(PICA::ShaderGen::FragmentGenerator& fragShaderGen, void* userdata) - : fragShaderGen(fragShaderGen) -{ - preallocatedProgramsIndex = 0; - running.store(true); +AsyncCompilerThread::AsyncCompilerThread(PICA::ShaderGen::FragmentGenerator& fragShaderGen, void* userdata) : fragShaderGen(fragShaderGen) { + preallocatedProgramsIndex = 0; + running.store(true); - for (int i = 0; i < preallocatedProgramsSize; i++) - { - preallocatedPrograms[i] = new CompilingProgram(); - preallocatedPrograms[i]->config = new PICA::FragmentConfig({}); - } + for (int i = 0; i < preallocatedProgramsSize; i++) { + preallocatedPrograms[i] = new CompilingProgram(); + preallocatedPrograms[i]->config = new PICA::FragmentConfig({}); + } - // The context needs to be created on the main thread so that we can make it shared with that - // thread's context - void* context = AsyncCompiler::createContext(userdata); - thread = std::thread([this, userdata, context]() - { - AsyncCompiler::makeCurrent(userdata, context); - printf("Async compiler started, GL version: %s\n", glGetString(GL_VERSION)); + // The context needs to be created on the main thread so that we can make it shared with that + // thread's context + void* context = AsyncCompiler::createContext(userdata); + thread = std::thread([this, userdata, context]() { + AsyncCompiler::makeCurrent(userdata, context); + printf("Async compiler started, GL version: %s\n", glGetString(GL_VERSION)); - std::string defaultShadergenVSSource = this->fragShaderGen.getDefaultVertexShader(); - defaultShadergenVs.create({defaultShadergenVSSource.c_str(), defaultShadergenVSSource.size()}, OpenGL::Vertex); + std::string defaultShadergenVSSource = this->fragShaderGen.getDefaultVertexShader(); + defaultShadergenVs.create({defaultShadergenVSSource.c_str(), defaultShadergenVSSource.size()}, OpenGL::Vertex); - while (running.load()) - { - CompilingProgram* item; - while (programQueue.Pop(item)) { - OpenGL::Program& glProgram = item->program->program; - std::string fs = this->fragShaderGen.generate(*item->config); - OpenGL::Shader fragShader({fs.c_str(), fs.size()}, OpenGL::Fragment); - glProgram.create({defaultShadergenVs, fragShader}); - item->program->compiling.store(false); - fragShader.free(); - } - - hasWork.clear(); - std::this_thread::yield(); - } + while (running.load()) { + CompilingProgram* item; + while (programQueue.Pop(item)) { + OpenGL::Program& glProgram = item->program->program; + std::string fs = this->fragShaderGen.generate(*item->config); + OpenGL::Shader fragShader({fs.c_str(), fs.size()}, OpenGL::Fragment); + glProgram.create({defaultShadergenVs, fragShader}); + item->program->compiling.store(false); + fragShader.free(); + } - AsyncCompiler::destroyContext(context); - }); + hasWork.clear(); + std::this_thread::yield(); + } + + AsyncCompiler::destroyContext(context); + }); } -AsyncCompilerThread::~AsyncCompilerThread() -{ - running.store(false); - thread.join(); +AsyncCompilerThread::~AsyncCompilerThread() { + running.store(false); + thread.join(); - for (int i = 0; i < preallocatedProgramsSize; i++) - { - delete preallocatedPrograms[i]->config; - delete preallocatedPrograms[i]; - } + for (int i = 0; i < preallocatedProgramsSize; i++) { + delete preallocatedPrograms[i]->config; + delete preallocatedPrograms[i]; + } } -void AsyncCompilerThread::PushFragmentConfig(const PICA::FragmentConfig& config, CachedProgram* cachedProgram) -{ - CompilingProgram* newProgram = preallocatedPrograms[preallocatedProgramsIndex]; - newProgram->program = cachedProgram; - *newProgram->config = config; - preallocatedProgramsIndex = (preallocatedProgramsIndex + 1) % preallocatedProgramsSize; - bool pushed = programQueue.Push(newProgram); +void AsyncCompilerThread::PushFragmentConfig(const PICA::FragmentConfig& config, CachedProgram* cachedProgram) { + CompilingProgram* newProgram = preallocatedPrograms[preallocatedProgramsIndex]; + newProgram->program = cachedProgram; + *newProgram->config = config; + preallocatedProgramsIndex = (preallocatedProgramsIndex + 1) % preallocatedProgramsSize; + bool pushed = programQueue.Push(newProgram); - if (!pushed) { - Helpers::warn("AsyncCompilerThread: Queue full, spinning"); - } else { - return; - } + if (!pushed) { + Helpers::warn("AsyncCompilerThread: Queue full, spinning"); + } else { + return; + } - while (!pushed) { - pushed = programQueue.Push(newProgram); - } + while (!pushed) { + pushed = programQueue.Push(newProgram); + } } -void AsyncCompilerThread::Finish() -{ - hasWork.test_and_set(); +void AsyncCompilerThread::Finish() { + hasWork.test_and_set(); - // Wait for the compiler thread to finish any outstanding work - while (hasWork.test_and_set()) {} + // Wait for the compiler thread to finish any outstanding work + while (hasWork.test_and_set()) { + } } \ No newline at end of file diff --git a/src/jni_driver.cpp b/src/jni_driver.cpp index 3f8c7eef..306c8ba6 100644 --- a/src/jni_driver.cpp +++ b/src/jni_driver.cpp @@ -4,10 +4,10 @@ #include +#include "android_utils.hpp" #include "emulator.hpp" #include "renderer_gl/renderer_gl.hpp" #include "services/hid.hpp" -#include "android_utils.hpp" std::unique_ptr emulator = nullptr; HIDService* hidService = nullptr; @@ -40,17 +40,17 @@ JNIEnv* jniEnv() { extern "C" { #define MAKE_SETTING(functionName, type, settingName) \ -AlberFunction(void, functionName) (JNIEnv* env, jobject obj, type value) { emulator->getConfig().settingName = value; } + AlberFunction(void, functionName)(JNIEnv * env, jobject obj, type value) { emulator->getConfig().settingName = value; } MAKE_SETTING(setShaderJitEnabled, jboolean, shaderJitEnabled) #undef MAKE_SETTING AlberFunction(void, Setup)(JNIEnv* env, jobject obj) { - env->GetJavaVM(&jvm); + env->GetJavaVM(&jvm); - alberClass = (jclass)env->NewGlobalRef((jclass)env->FindClass("com/panda3ds/pandroid/AlberDriver")); - alberClassOpenDocument = env->GetStaticMethodID(alberClass, "openDocument", "(Ljava/lang/String;Ljava/lang/String;)I"); + alberClass = (jclass)env->NewGlobalRef((jclass)env->FindClass("com/panda3ds/pandroid/AlberDriver")); + alberClassOpenDocument = env->GetStaticMethodID(alberClass, "openDocument", "(Ljava/lang/String;Ljava/lang/String;)I"); } AlberFunction(void, Pause)(JNIEnv* env, jobject obj) { emulator->pause(); } @@ -128,17 +128,17 @@ AlberFunction(jbyteArray, GetSmdh)(JNIEnv* env, jobject obj) { #undef AlberFunction int AndroidUtils::openDocument(const char* path, const char* perms) { - auto env = jniEnv(); + auto env = jniEnv(); - jstring uri = env->NewStringUTF(path); - jstring jmode = env->NewStringUTF(perms); + jstring uri = env->NewStringUTF(path); + jstring jmode = env->NewStringUTF(perms); - jint result = env->CallStaticIntMethod(alberClass, alberClassOpenDocument, uri, jmode); + jint result = env->CallStaticIntMethod(alberClass, alberClassOpenDocument, uri, jmode); - env->DeleteLocalRef(uri); - env->DeleteLocalRef(jmode); + env->DeleteLocalRef(uri); + env->DeleteLocalRef(jmode); - return (int)result; + return (int)result; } namespace AsyncCompiler { @@ -147,9 +147,7 @@ namespace AsyncCompiler { return nullptr; } - void makeCurrent(void* mainContext, void* context) { - } + void makeCurrent(void* mainContext, void* context) {} - void destroyContext(void* context) { - } -} + void destroyContext(void* context) {} +} // namespace AsyncCompiler diff --git a/src/libretro_core.cpp b/src/libretro_core.cpp index 6b14cbc3..8da43d4c 100644 --- a/src/libretro_core.cpp +++ b/src/libretro_core.cpp @@ -1,11 +1,10 @@ -#include -#include -#include - #include +#include #include +#include #include +#include static retro_environment_t envCallbacks; static retro_video_refresh_t videoCallbacks; @@ -21,17 +20,11 @@ static bool screenTouched; std::unique_ptr emulator; RendererGL* renderer; -std::filesystem::path Emulator::getConfigPath() { - return std::filesystem::path(savePath / "config.toml"); -} +std::filesystem::path Emulator::getConfigPath() { return std::filesystem::path(savePath / "config.toml"); } -std::filesystem::path Emulator::getAppDataRoot() { - return std::filesystem::path(savePath / "Emulator Files"); -} +std::filesystem::path Emulator::getAppDataRoot() { return std::filesystem::path(savePath / "Emulator Files"); } -static void* GetGLProcAddress(const char* name) { - return (void*)hw_render.get_proc_address(name); -} +static void* GetGLProcAddress(const char* name) { return (void*)hw_render.get_proc_address(name); } static void VideoResetContext() { #ifdef USING_GLES @@ -47,9 +40,7 @@ static void VideoResetContext() { emulator->initGraphicsContext(nullptr); } -static void VideoDestroyContext() { - emulator->deinitGraphicsContext(); -} +static void VideoDestroyContext() { emulator->deinitGraphicsContext(); } static bool SetHWRender(retro_hw_context_type type) { hw_render.context_type = type; @@ -142,16 +133,14 @@ static std::string FetchVariable(std::string key, std::string def) { return std::string(var.value); } -static bool FetchVariableBool(std::string key, bool def) { - return FetchVariable(key, def ? "enabled" : "disabled") == "enabled"; -} +static bool FetchVariableBool(std::string key, bool def) { return FetchVariable(key, def ? "enabled" : "disabled") == "enabled"; } static void configInit() { static const retro_variable values[] = { {"panda3ds_use_shader_jit", "Enable shader JIT; enabled|disabled"}, {"panda3ds_accurate_shader_mul", "Enable accurate shader multiplication; disabled|enabled"}, {"panda3ds_use_ubershader", defaultShaderMode == ShaderMode::Ubershader ? "Use ubershaders (No stutter, maybe slower); enabled|disabled" - : "Use ubershaders (No stutter, maybe slower); disabled|enabled"}, + : "Use ubershaders (No stutter, maybe slower); disabled|enabled"}, {"panda3ds_use_vsync", "Enable VSync; enabled|disabled"}, {"panda3ds_dsp_emulation", "DSP emulation; Null|HLE|LLE"}, {"panda3ds_use_audio", "Enable audio; disabled|enabled"}, @@ -180,7 +169,9 @@ static void configUpdate() { config.sdCardInserted = FetchVariableBool("panda3ds_use_virtual_sd", true); config.sdWriteProtected = FetchVariableBool("panda3ds_write_protect_virtual_sd", false); config.accurateShaderMul = FetchVariableBool("panda3ds_accurate_shader_mul", false); - config.shaderMode = FetchVariableBool("panda3ds_use_ubershader", EmulatorConfig::defaultShaderMode == ShaderMode::Ubershader) ? ShaderMode::Ubershader : ShaderMode::Specialized; + config.shaderMode = FetchVariableBool("panda3ds_use_ubershader", EmulatorConfig::defaultShaderMode == ShaderMode::Ubershader) + ? ShaderMode::Ubershader + : ShaderMode::Specialized; config.forceShadergenForLights = FetchVariableBool("panda3ds_ubershader_lighting_override", true); config.lightShadergenThreshold = std::clamp(std::stoi(FetchVariable("panda3ds_ubershader_lighting_override_threshold", "1")), 1, 8); config.discordRpcEnabled = false; @@ -217,27 +208,17 @@ void retro_get_system_av_info(retro_system_av_info* info) { info->timing.sample_rate = 32768; } -void retro_set_environment(retro_environment_t cb) { - envCallbacks = cb; -} +void retro_set_environment(retro_environment_t cb) { envCallbacks = cb; } -void retro_set_video_refresh(retro_video_refresh_t cb) { - videoCallbacks = cb; -} +void retro_set_video_refresh(retro_video_refresh_t cb) { videoCallbacks = cb; } -void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { - audioBatchCallback = cb; -} +void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audioBatchCallback = cb; } void retro_set_audio_sample(retro_audio_sample_t cb) {} -void retro_set_input_poll(retro_input_poll_t cb) { - inputPollCallback = cb; -} +void retro_set_input_poll(retro_input_poll_t cb) { inputPollCallback = cb; } -void retro_set_input_state(retro_input_state_t cb) { - inputStateCallback = cb; -} +void retro_set_input_state(retro_input_state_t cb) { inputStateCallback = cb; } void retro_init() { enum retro_pixel_format xrgb888 = RETRO_PIXEL_FORMAT_XRGB8888; @@ -255,9 +236,7 @@ void retro_init() { emulator = std::make_unique(); } -void retro_deinit() { - emulator = nullptr; -} +void retro_deinit() { emulator = nullptr; } bool retro_load_game(const retro_game_info* game) { configInit(); @@ -283,9 +262,7 @@ void retro_unload_game() { renderer = nullptr; } -void retro_reset() { - emulator->reset(Emulator::ReloadOption::Reload); -} +void retro_reset() { emulator->reset(Emulator::ReloadOption::Reload); } void retro_run() { ConfigCheckVariables(); @@ -400,6 +377,4 @@ void retro_cheat_set(uint index, bool enabled, const char* code) { } } -void retro_cheat_reset() { - emulator->getCheats().reset(); -} +void retro_cheat_reset() { emulator->getCheats().reset(); } diff --git a/src/panda_qt/main_window.cpp b/src/panda_qt/main_window.cpp index 5cf08648..e0e76d05 100644 --- a/src/panda_qt/main_window.cpp +++ b/src/panda_qt/main_window.cpp @@ -631,4 +631,4 @@ namespace AsyncCompiler { std::unique_ptr* glContext = static_cast*>(context); delete glContext; } -} \ No newline at end of file +} // namespace AsyncCompiler \ No newline at end of file diff --git a/src/renderer.cpp b/src/renderer.cpp index b9deb866..9399133d 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -43,9 +43,13 @@ std::optional Renderer::shaderModeFromString(std::string inString) { std::transform(inString.begin(), inString.end(), inString.begin(), [](unsigned char c) { return std::tolower(c); }); static const std::unordered_map map = { - {"specialized", ShaderMode::Specialized}, {"special", ShaderMode::Specialized}, - {"ubershader", ShaderMode::Ubershader}, {"uber", ShaderMode::Ubershader}, - {"hybrid", ShaderMode::Hybrid}, {"threaded", ShaderMode::Hybrid}, {"i hate opengl context creation", ShaderMode::Hybrid}, + {"specialized", ShaderMode::Specialized}, + {"special", ShaderMode::Specialized}, + {"ubershader", ShaderMode::Ubershader}, + {"uber", ShaderMode::Ubershader}, + {"hybrid", ShaderMode::Hybrid}, + {"threaded", ShaderMode::Hybrid}, + {"i hate opengl context creation", ShaderMode::Hybrid}, }; if (auto search = map.find(inString); search != map.end()) {