Ejemplo n.º 1
0
int main(int argc, char **argv)
{
    // init global vars
    initSharedMem();

    // register exit callback
    atexit(exitCB);

    // init GLUT and GL
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL);
    glutInitWindowSize(screenWidth, screenHeight);
    glutInitWindowPosition(200, 200);
    int handle = glutCreateWindow(argv[0]);
    glutDisplayFunc(displayCB);
    glutReshapeFunc(reshapeCB);
    glutKeyboardFunc(keyboardCB);

    glewInit();
    if (glewIsSupported("GL_VERSION_2_1"))
    {
        MYLOG("Ready for OpenGL 2.1\n");
    }
    else
    {
        MYLOG("OpenGL 2.1 not supported\n");
        exit(1);
    }
    if (GLEW_ARB_vertex_shader && GLEW_ARB_fragment_shader && GL_EXT_geometry_shader4)
    {
        MYLOG("Ready for GLSL - vertex, fragment, and geometry units\n");
    }
    else
    {
        MYLOG("Not totally ready :( \n");
        exit(1);
    }

    initRS();
    glutMainLoop();

    return 0;
}
Ejemplo n.º 2
0
int main(int argc, char **argv) {
	ACQ_MessagePacketType *packet;
	int currentPacket, intSlot;
	int lastId;
	int numChannels, numSamples, sampleNumber;
	host_t host;
	pthread_t tcpserverThread, convertThread;
	
	/* Parse command line arguments */
	if (argc == 1) {
		/* Put defaults in case no arguments given */
		strcpy(host.name, "-");
		host.port = 1972;
	} else if (argc == 3) {
		strncpy(host.name, argv[1], sizeof(host.name));
		host.name[sizeof(host.name)-1]=0;
		
		host.port = atoi(argv[2]);
		if (host.port <= 0) {
			fputs("Port number must be positive\n\n",stderr);
			fputs(usage, stderr);
			return 1;
		}
	} else {
		fputs(usage, stderr);
		return 1;
	}
	
	/* Create socket pair */
	if (socketpair(AF_UNIX, SOCK_STREAM, 0, mySockets)) {
		perror("Could not create socket pair for internal communication: ");
		return 1;
	}
	
	/*  Internal ringbuffer uses same mechanism as Acq: free slots are
		marked with ACQ_MSGQ_INVALID. To avoid polling in the converter thread,
		the main thread sends the index of the latest slot over the socket pair.
		If the converter thread does not manage to clear the ringbuffer quickly
		enough, the program will report an error. */
	for (intSlot=0;intSlot<INT_RB_SIZE;intSlot++) {
		intPackets[intSlot].message_type = ACQ_MSGQ_INVALID;
	}
	
	/* Spawn conversion thread */
	if (pthread_create(&convertThread, NULL, dataToFieldTripThread, NULL)) {
		fprintf(stderr, "Could not spawn conversion thread\n");
		return 1;
	}
	
	/* Spawn tcpserver or connect to remote buffer */
	if (strcmp(host.name, "-") == 0) {
		int rc;
		
		rc = pthread_create(&tcpserverThread, NULL, tcpserver, &host);
		if (rc) {
			fprintf(stderr, "Could not spawn tcpserver thread (%d)\n", rc);
			return 1;
		}
		ftSocket = 0; /* dma */
	} else {
		ftSocket = open_connection(host.name, host.port);
		
		if (ftSocket < 0) {
			fprintf(stderr, "Could not connect to remote buffer\n");
			return 1;
		}
	}		
	
	printf("Trying to set up shared memory...\n");
	packet = createSharedMem();
	if (packet == NULL) {
		fprintf(stderr, "Could not set up shared memory - exiting\n");
		exit(1);
	}
	initSharedMem(packet);
	
	/* register CTRL-C handler */
	signal(SIGINT, abortHandler);
	
	/* Both ringbuffers start at 0 */
	currentPacket = intSlot = 0;
	
	printf("Entering main loop. Press CTRL-C to stop operation.\n\n");
	while (keepRunning) {
		int size;
		char *dataset;
		int code = waitPacket(&packet[currentPacket].message_type);
		
		switch(code) {
			case ACQ_MSGQ_SETUP_COLLECTION:
				lastId = packet[currentPacket].messageId;
				dataset = (char *) packet[currentPacket].data;
				printf("Setup | ID=%i | %.255s\n", lastId, dataset);
				
				size = 5*sizeof(int) + strlen(dataset) + 1;
				if (size >= sizeof(ACQ_MessagePacketType)) {
					fprintf(stderr, "Dataset name not zero-terminated -- ignoring\n");
					continue;
				}
				
				if (intPackets[intSlot].message_type != ACQ_MSGQ_INVALID) {
					fprintf(stderr, "Internal converter thread does not keep up with the load.\n");
				} else {
					memcpy(&intPackets[intSlot], &packet[currentPacket], size);
					write(mySockets[0], &intSlot, sizeof(int));
					if (++intSlot == INT_RB_SIZE) intSlot=0;
				}
				
				packet[currentPacket].message_type = ACQ_MSGQ_INVALID;
				if (++currentPacket == ACQ_MSGQ_SIZE) currentPacket=0;
				break;
			case ACQ_MSGQ_DATA:
				lastId = packet[currentPacket].messageId;
				numChannels  = packet[currentPacket].numChannels;
				numSamples   = packet[currentPacket].numSamples;
				sampleNumber = packet[currentPacket].sampleNumber;
				printf("Data | %3i channels x %3i samples | nr = %6i | ID=%4i | slot=%3i\n", numChannels, numSamples, sampleNumber, lastId, currentPacket);
								
				size = 5*sizeof(int) + numSamples*numChannels*sizeof(int);
				
				if (size > sizeof(ACQ_OverAllocType)) {
					fprintf(stderr, "Acq wrote far too much data -- cannot handle this\n");
				} else {
					if (intPackets[intSlot].message_type != ACQ_MSGQ_INVALID) {
						fprintf(stderr, "Internal converter thread does not keep up with the load.\n");
					} else {
						memcpy(&intPackets[intSlot], &packet[currentPacket], size);
						write(mySockets[0], &intSlot, sizeof(int));
						if (++intSlot == INT_RB_SIZE) intSlot=0;
					}
				}				
				
				if (numSamples * numChannels > 28160) {
					fprintf(stderr, "Warning: Acq wrote too much data into this block!\n");
            
					/* clear this packet */
					packet[currentPacket].message_type = ACQ_MSGQ_INVALID;
					/* next packet last in ringbuffer? */ 
					if (++currentPacket == ACQ_MSGQ_SIZE) {
						currentPacket = 0;
					} else {
						/* make sure the next packet is marked as free */
						packet[currentPacket].message_type = ACQ_MSGQ_INVALID;
					}
				} else {
					packet[currentPacket].message_type = ACQ_MSGQ_INVALID;
					if (++currentPacket == ACQ_MSGQ_SIZE) currentPacket=0;
				}
				break;
			case ACQ_MSGQ_CLOSE_CONNECTION:
				printf("Stop | ID=%i \n", lastId);
				
				/* Does this mean anything for the buffer as well ? */
				
				packet[currentPacket].message_type = ACQ_MSGQ_INVALID;
				currentPacket = 0;
				break;
			case ACQ_MSGQ_INVALID:
				printf("Waiting (on %i)\n",currentPacket);
				break;
			default:
				fprintf(stderr, "Warning: Unexpected ID in packet %i\n", currentPacket);
				break;
		}
	}
	printf("Closing sockets / stopping tcpserver...\n");
	close(mySockets[0]); /* the other one will be closed in the thread */
	if (ftSocket > 0) {
		close_connection(ftSocket);
	} else {
		pthread_cancel(tcpserverThread);
		pthread_detach(tcpserverThread);
	}
	printf("Closing shared memory...\n");
	closeSharedMem(packet);
	printf("Joining conversion thread...\n");
	pthread_join(convertThread, NULL);
	printf("Done.\n");
	return 0;
}
Ejemplo n.º 3
0
int main(int argc, char **argv) {
    ACQ_MessagePacketType *packet;
    int currentPacket = 0;
    int numChannels = 356;
    int blockSize = 70;  /* > 28160/356 is too much for regular operation! */
    int sampleNumber = 0;
    int ID = 0;
    int sampleFreq = 1200;
    struct timeval timerInterval = {0,50000}; /*  50ms */
    struct timeval timerValue    = {0,50000};  /* 200ms */
    struct itimerval timerOpts;

    timerOpts.it_value = timerValue;
    timerOpts.it_interval = timerInterval;

    if (argc>=2) {
        sampleFreq = atoi(argv[1]);
        if (sampleFreq>0) {
            timerOpts.it_interval.tv_usec = timerOpts.it_value.tv_usec = 1000000*blockSize/sampleFreq;
        }
    }

    printf("Trying to set up shared memory...\n");
    packet = createSharedMem();
    if (packet == NULL) {
        fprintf(stderr, "Could not set up shared memory - exiting\n");
        exit(1);
    }
    initSharedMem(packet);

    packet[0].messageId    = 0;
    packet[0].sampleNumber = 0;
    packet[0].numSamples   = 0;
    packet[0].numChannels  = numChannels;
    strcpy((char *)(packet[0].data), "test.ds");
    packet[0].message_type = ACQ_MSGQ_SETUP_COLLECTION;

    currentPacket = 1;

    printf("Wrote setup packet at 0\n");


    usleep(200000);  /* 0.2 sec */
    setitimer(ITIMER_REAL, &timerOpts, NULL);
    signal(SIGALRM, alarmHandler);
    /* register CTRL-C handler */
    signal(SIGINT,  abortHandler);


    while (keepRunning) {
        ++ID;

        if (packet[currentPacket].message_type != ACQ_MSGQ_INVALID) {
            printf("Packet #%i not free...\n", currentPacket);
            break;
        }

        packet[currentPacket].messageId = ID;
        packet[currentPacket].sampleNumber = sampleNumber;
        packet[currentPacket].numSamples = blockSize;
        packet[currentPacket].numChannels = numChannels;
        memset(packet[currentPacket].data, 0, numChannels * blockSize * sizeof(int));
        //packet[currentPacket].data[0] = i+1;
        //packet[currentPacket].data[10*numChannels] = 10;
        packet[currentPacket].numChannels = numChannels;
        packet[currentPacket].message_type = ACQ_MSGQ_DATA;

        printf("Wrote data packet %i at %i\n", ID, currentPacket);

        sampleNumber += blockSize;

        if (++currentPacket == ACQ_MSGQ_SIZE) currentPacket = 0;

        sleep(1);
    }

    packet[currentPacket].message_type = ACQ_MSGQ_CLOSE_CONNECTION;
    usleep(100000);

    closeSharedMem(packet);
    return 0;
}
Ejemplo n.º 4
0
int main(int argc, char* argv[])
{
    struct sockaddr_in client;

	//Validate input
    if (argc != 2)
    {
        cout << "Usage: panserver <portnumber>" << endl;
        return -1;
    }	
	// initialize shared memory
	initSharedMem();
    // Initialize the ports
    panel.server.sin_port = htons(atoi(argv[1]));
    audience.server.sin_port = htons(atoi(argv[1])+1);

    /** Create socket on which to send  and receive */

    if ( panel.CreatSocket() < 0 ) // create panel socket
        return -1;

    if ( audience.CreatSocket() < 0 ) // create panel socket
        return -1;
	
    reusePort(panel.socket_s); //reuse the binding port when execute program
    
	if ( panel.BindConnection() < 0 ) // Check for binding errors
        return -1;
    
	reusePort(audience.socket_s);//reuse the binding port when execute program
	
    if ( audience.BindConnection() < 0 ) // Check for binding errors
        return -1;

    /** get panel port information and prints it out */
    if ( panel.GetSocketName() ) // check socket name validity
        return -1;

    /** get audience port information and prints it out */
    if ( audience.GetSocketName() ) // check socket name validity
        return -1;
	
    /** accept TCP connections from clients and fork a process to serve each */
    listen(panel.socket_s,10);
    listen(audience.socket_s,10);
	
	for(;;){
        audience_pid = fork();
        
        /* This is the audience server */
        if ( audience_pid == 0) {
			
			signal (SIGQUIT, SIG_IGN);
			signal (SIGINT, SIG_IGN);
			signal (SIGTSTP,SIG_IGN);
			signal (SIGTERM,SIG_IGN);
			
			 	
			
            cout << "Forked AUDIENCE Server, PID=" << getpid() << endl;
            ServAudience(audience, client);
        }
        else
        {		
			//This is to handle the quit function
			if (signal(SIGQUIT, CloseMeeting) == SIG_ERR) {
				fprintf(stderr, "cannot set handler for SIGQUIT\n");
            }
			
			//list people in the room
			if (signal(SIGINT, inthandle) == SIG_ERR) {
                fprintf(stderr, "cannot set handler for SIGINT\n");
            }
			
			//Handle dead signal
            if (signal(SIGTERM, SomeoneIsLeaving) == SIG_ERR) {
                fprintf(stderr, "cannot set handler for SIGTERM\n");
            }
			
			//Handle ctrl+Z 
			 if (signal(SIGTSTP, AllowQA) == SIG_ERR) {
                fprintf(stderr, "cannot set handler for SIGTSTP\n");
            }
			
            cout << "AUDIENCE server started at port: (" << ntohs(audience.server.sin_port) << ")" << endl;
            cout << "PANELIST server started at port: (" << ntohs(panel.server.sin_port) << ")" << endl;          
			ServPanelist(panel, client);
        }
       wait(NULL); 
    }
}
Ejemplo n.º 5
0
 int main (int argc, char **argv) {
  #ifdef CLEAR
  strcpy(table_path, dirname(argv[0]));
  strcat(table_path, "/");
  strcat(table_path, argv[2]);
  shm_unlink(table_path);
  #else
  // verifies if the number of arguments is correct
  if (argc != 4) {
    printf("usage: %s <player's name> <table's name> <nr. players>\n", argv[0]);
    return -1;
  }
  
  if (verifyCmdArgs(argv) == -1) {
    return -1;
  }
  
  // installs an exit handler
  if (atexit(exitHandler) == -1) {
    perror("atexit()");
    exit(-1);
  }
  
  blockSignals();
  
  initFIFO(argv[1]);
  
  initSharedMem(argv);
  
  waitForPlayers();
  
  pthread_t tid;
  
  if (is_dealer) {

    initDefaultDeck();
    printCardsList(cards, NULL);
    shuffleDeck();
    randomiseFirstPlayer();
    
    if ((errno = pthread_create(&tid, NULL, dealCards, NULL)) != 0) {
      perror("pthread_create()");
      exit(-1);
    }
  }
  receiveCards();
  reorderCardsList(hand);
  
  if (is_dealer) {
    pthread_join(tid, NULL);
  }
  //call thread responsible for showing info about the game and manage the game
  pthread_t tidG;
  if ((errno = pthread_create(&tidG, NULL, playGame, NULL)) != 0) {
    perror("pthread_create()");
    exit(-1);
  }
  
  pthread_join(tidG, NULL);
  
  #endif
  return 0;
}
Ejemplo n.º 6
0
Archivo: main.cpp Proyecto: hgl888/glfw
int main(int argc, char **argv)
{
    initSharedMem();

    // register exit callback
    atexit(exitCB);

    // init GLUT and GL
    initGLUT(argc, argv);
    initGL();

    // get OpenGL info
    glInfo glInfo;
    glInfo.getInfo();
    glInfo.printSelf();

#ifdef _WIN32
    // check PBO is supported by your video card
    if(glInfo.isExtensionSupported("GL_ARB_pixel_buffer_object"))
    {
        // get pointers to GL functions
        glGenBuffersARB = (PFNGLGENBUFFERSARBPROC)wglGetProcAddress("glGenBuffersARB");
        glBindBufferARB = (PFNGLBINDBUFFERARBPROC)wglGetProcAddress("glBindBufferARB");
        glBufferDataARB = (PFNGLBUFFERDATAARBPROC)wglGetProcAddress("glBufferDataARB");
        glBufferSubDataARB = (PFNGLBUFFERSUBDATAARBPROC)wglGetProcAddress("glBufferSubDataARB");
        glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)wglGetProcAddress("glDeleteBuffersARB");
        glGetBufferParameterivARB = (PFNGLGETBUFFERPARAMETERIVARBPROC)wglGetProcAddress("glGetBufferParameterivARB");
        glMapBufferARB = (PFNGLMAPBUFFERARBPROC)wglGetProcAddress("glMapBufferARB");
        glUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC)wglGetProcAddress("glUnmapBufferARB");

        // check once again PBO extension
        if(glGenBuffersARB && glBindBufferARB && glBufferDataARB && glBufferSubDataARB &&
           glMapBufferARB && glUnmapBufferARB && glDeleteBuffersARB && glGetBufferParameterivARB)
        {
            pboSupported = pboUsed = true;
            std::cout << "Video card supports GL_ARB_pixel_buffer_object." << std::endl;
        }
        else
        {
            pboSupported = pboUsed = false;
            std::cout << "Video card does NOT support GL_ARB_pixel_buffer_object." << std::endl;
        }
    }

    // check EXT_swap_control is supported
    if(glInfo.isExtensionSupported("WGL_EXT_swap_control"))
    {
        // get pointers to WGL functions
        wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT");
        wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC)wglGetProcAddress("wglGetSwapIntervalEXT");
        if(wglSwapIntervalEXT && wglGetSwapIntervalEXT)
        {
            // disable v-sync
            wglSwapIntervalEXT(0);
            std::cout << "Video card supports WGL_EXT_swap_control." << std::endl;
        }
    }

#else // for linux, do not need to get function pointers, it is up-to-date
    if(glInfo.isExtensionSupported("GL_ARB_pixel_buffer_object"))
    {
        pboSupported = pboUsed = true;
        std::cout << "Video card supports GL_ARB_pixel_buffer_object." << std::endl;
    }
    else
    {
        pboSupported = pboUsed = false;
        std::cout << "Video card does NOT support GL_ARB_pixel_buffer_object." << std::endl;
    }
#endif

    if(pboSupported)
    {
        // create 2 pixel buffer objects, you need to delete them when program exits.
        // glBufferDataARB with NULL pointer reserves only memory space.
        glGenBuffersARB(PBO_COUNT, pboIds);
        glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pboIds[0]);
        glBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, DATA_SIZE, 0, GL_STREAM_READ_ARB);
        glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pboIds[1]);
        glBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, DATA_SIZE, 0, GL_STREAM_READ_ARB);

        glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);
    }

    // start timer, the elapsed time will be used for updateVertices()
    timer.start();

    // the last GLUT call (LOOP)
    // window will be shown and display callback is triggered by events
    // NOTE: this call never return main().
    glutMainLoop(); /* Start GLUT event-processing loop */

    return 0;
}
Ejemplo n.º 7
0
int main(int argc, char **argv) {
    gfxInitDefault();

    PrintConsole botScreen;
    PrintConsole topScreen;

    consoleInit(GFX_TOP, &topScreen);
    consoleInit(GFX_BOTTOM, &botScreen);
    consoleSelect(&topScreen);

    constexpr size_t NUM_SAMPLES = 160*200;
    u32 *audio_buffer = (u32*)linearAlloc(NUM_SAMPLES * sizeof(u32));
    fillBuffer(audio_buffer, NUM_SAMPLES);

    AudioState state;
    {
        auto dspfirm = loadDspFirmFromFile();
        if (!dspfirm) {
            printf("Couldn't load firmware\n");
            goto end;
        }
        auto ret = audioInit(*dspfirm);
        if (!ret) {
            printf("Couldn't init audio\n");
            goto end;
        }
        state = *ret;
    }

    state.waitForSync();
    initSharedMem(state);
    state.write().dsp_configuration->mixer1_enabled_dirty = true;
    state.write().dsp_configuration->mixer1_enabled = true;
    //state.write().dsp_configuration->limiter_enabled = 1;
    state.notifyDsp();
    printf("init\n");

    state.waitForSync();
    state.notifyDsp();
    state.waitForSync();
    for (auto& gain : state.write().source_configurations->config[0].gain) {
        for (auto& g : gain) {
            g = 0.0;
        }
    }
    state.write().source_configurations->config[0].gain[0][0] = 1.0;
    state.write().source_configurations->config[0].gain[1][0] = 1.0;
    state.write().source_configurations->config[0].gain[1][1] = 0.5;
    state.write().source_configurations->config[0].gain_1_dirty = true;
    state.notifyDsp();
    state.waitForSync();
    state.notifyDsp();
    state.waitForSync();
    state.notifyDsp();

    {
        while (true) {
            state.waitForSync();
            printf("sync = %i, play = %i, cbi = %i\n", state.read().source_statuses->status[0].sync, state.read().source_statuses->status[0].is_enabled, state.read().source_statuses->status[0].current_buffer_id);
            if (state.read().source_statuses->status[0].sync == 1) break;
            state.notifyDsp();
        }
        printf("fi: %i\n", state.frame_id);

        u16 buffer_id = 0;
        //unsigned next_queue_position = 0;

        state.write().source_configurations->config[0].play_position = 0;
        state.write().source_configurations->config[0].physical_address = osConvertVirtToPhys(audio_buffer);
        state.write().source_configurations->config[0].length = NUM_SAMPLES;
        state.write().source_configurations->config[0].mono_or_stereo = DSP::HLE::SourceConfiguration::Configuration::MonoOrStereo::Stereo;
        state.write().source_configurations->config[0].format = DSP::HLE::SourceConfiguration::Configuration::Format::PCM16;
        state.write().source_configurations->config[0].fade_in = false;
        state.write().source_configurations->config[0].adpcm_dirty = false;
        state.write().source_configurations->config[0].is_looping = false;
        state.write().source_configurations->config[0].buffer_id = ++buffer_id;
        state.write().source_configurations->config[0].partial_reset_flag = true;
        state.write().source_configurations->config[0].play_position_dirty = true;
        state.write().source_configurations->config[0].embedded_buffer_dirty = true;

        state.write().source_configurations->config[0].enable = true;
        state.write().source_configurations->config[0].enable_dirty = true;

        state.notifyDsp();

        bool continue_reading = true;
        for (size_t frame_count = 0; continue_reading; frame_count++) {
            state.waitForSync();

            if (state.read().source_statuses->status[0].current_buffer_id) {
                printf("%i cbi = %i\n", frame_count, state.read().source_statuses->status[0].current_buffer_id);
            }

            for (size_t i = 0; i < 160; i++) {
                if (state.write().intermediate_mix_samples->mix1.pcm32[0][i]) {
                    printf("[intermediate] frame=%i, sample=%i\n", frame_count, i);
                    for (size_t j = 0; j < 20; j++) {
                        printf("%08lx ", (u32)state.write().intermediate_mix_samples->mix1.pcm32[0][j]);
                    }
                    printf("\n");
                    break;
                }
            }

            for (size_t i = 0; i < 160 * 2; i++) {
                if (state.read().final_samples->pcm16[i]) {
                    printf("[final] frame=%i, sample=%i\n", frame_count, i);
                    for (size_t j = 0; j < 20; j++) {
                        printf("%04x ", (u16)state.read().final_samples->pcm16[j]);
                    }
                    printf("\n");
                    continue_reading = false;
                    break;
                }
            }

            state.notifyDsp();
        }

        state.waitForSync();
        state.write().source_configurations->config[0].sync = 2;
        state.write().source_configurations->config[0].sync_dirty = true;
        state.notifyDsp();

        while (true) {
            state.waitForSync();
            printf("sync = %i, play = %i\n", state.read().source_statuses->status[0].sync, state.read().source_statuses->status[0].is_enabled);
            if (state.read().source_statuses->status[0].sync == 2) break;
            state.notifyDsp();
        }
        state.notifyDsp();

        printf("Done!\n");
    }

end:
    audioExit(state);
    waitForKey();
    gfxExit();
    return 0;
}
Ejemplo n.º 8
0
Archivo: main.cpp Proyecto: hgl888/glfw
int main(int argc, char **argv)
{
    // init global vars
    initSharedMem();

    // register exit callback
    atexit(exitCB);

    // init GLUT and GL
    initGLUT(argc, argv);
    initGL();

    // create a texture object
    glGenTextures(1, &textureId);
    glBindTexture(GL_TEXTURE_2D, textureId);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
    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_GENERATE_MIPMAP, GL_TRUE); // automatic mipmap generation included in OpenGL v1.4
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
    glBindTexture(GL_TEXTURE_2D, 0);

    // get OpenGL info
    glInfo glInfo;
    glInfo.getInfo();
    //glInfo.printSelf();

#ifdef _WIN32
    // check if FBO is supported by your video card
    if(glInfo.isExtensionSupported("GL_ARB_framebuffer_object"))
    {
        // get pointers to GL functions
        glGenFramebuffers                     = (PFNGLGENFRAMEBUFFERSPROC)wglGetProcAddress("glGenFramebuffers");
        glDeleteFramebuffers                  = (PFNGLDELETEFRAMEBUFFERSPROC)wglGetProcAddress("glDeleteFramebuffers");
        glBindFramebuffer                     = (PFNGLBINDFRAMEBUFFERPROC)wglGetProcAddress("glBindFramebuffer");
        glCheckFramebufferStatus              = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)wglGetProcAddress("glCheckFramebufferStatus");
        glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)wglGetProcAddress("glGetFramebufferAttachmentParameteriv");
        glGenerateMipmap                      = (PFNGLGENERATEMIPMAPPROC)wglGetProcAddress("glGenerateMipmap");
        glFramebufferTexture1D                = (PFNGLFRAMEBUFFERTEXTURE1DPROC)wglGetProcAddress("glFramebufferTexture1D");
        glFramebufferTexture2D                = (PFNGLFRAMEBUFFERTEXTURE2DPROC)wglGetProcAddress("glFramebufferTexture2D");
        glFramebufferTexture3D                = (PFNGLFRAMEBUFFERTEXTURE3DPROC)wglGetProcAddress("glFramebufferTexture3D");
        glFramebufferTextureLayer             = (PFNGLFRAMEBUFFERTEXTURELAYERPROC)wglGetProcAddress("glFramebufferTextureLayer");
        glFramebufferRenderbuffer             = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)wglGetProcAddress("glFramebufferRenderbuffer");
        glIsFramebuffer                       = (PFNGLISFRAMEBUFFERPROC)wglGetProcAddress("glIsFramebuffer");
        glBlitFramebuffer                     = (PFNGLBLITFRAMEBUFFERPROC)wglGetProcAddress("glBlitFramebuffer");
        glGenRenderbuffers                    = (PFNGLGENRENDERBUFFERSPROC)wglGetProcAddress("glGenRenderbuffers");
        glDeleteRenderbuffers                 = (PFNGLDELETERENDERBUFFERSPROC)wglGetProcAddress("glDeleteRenderbuffers");
        glBindRenderbuffer                    = (PFNGLBINDRENDERBUFFERPROC)wglGetProcAddress("glBindRenderbuffer");
        glRenderbufferStorage                 = (PFNGLRENDERBUFFERSTORAGEPROC)wglGetProcAddress("glRenderbufferStorage");
        glRenderbufferStorageMultisample      = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)wglGetProcAddress("glRenderbufferStorageMultisample");
        glGetRenderbufferParameteriv          = (PFNGLGETRENDERBUFFERPARAMETERIVPROC)wglGetProcAddress("glGetRenderbufferParameteriv");
        glIsRenderbuffer                      = (PFNGLISRENDERBUFFERPROC)wglGetProcAddress("glIsRenderbuffer");

        // check once again FBO extension
        if(glGenFramebuffers && glDeleteFramebuffers && glBindFramebuffer && glCheckFramebufferStatus &&
           glGetFramebufferAttachmentParameteriv && glGenerateMipmap && glFramebufferTexture1D && glFramebufferTexture2D && glFramebufferTexture3D &&
           glFramebufferTextureLayer && glFramebufferRenderbuffer && glIsFramebuffer && glBlitFramebuffer &&
           glGenRenderbuffers && glDeleteRenderbuffers && glBindRenderbuffer && glRenderbufferStorage &&
           glRenderbufferStorageMultisample && glGetRenderbufferParameteriv && glIsRenderbuffer)
        {
            fboSupported = fboUsed = true;
            std::cout << "Video card supports GL_ARB_framebuffer_object." << std::endl;
        }
        else
        {
            fboSupported = fboUsed = false;
            std::cout << "Video card does NOT support GL_ARB_framebuffer_object." << std::endl;
        }
    }

    // check EXT_swap_control is supported
    if(glInfo.isExtensionSupported("WGL_EXT_swap_control"))
    {
        // get pointers to WGL functions
        wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT");
        wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC)wglGetProcAddress("wglGetSwapIntervalEXT");
        if(wglSwapIntervalEXT && wglGetSwapIntervalEXT)
        {
            // disable v-sync
            wglSwapIntervalEXT(0);
            std::cout << "Video card supports WGL_EXT_swap_control." << std::endl;
        }
    }

#else // for linux, do not need to get function pointers, it is up-to-date
    if(glInfo.isExtensionSupported("GL_ARB_framebuffer_object"))
    {
        fboSupported = fboUsed = true;
        std::cout << "Video card supports GL_ARB_framebuffer_object." << std::endl;
    }
    else
    {
        fboSupported = fboUsed = false;
        std::cout << "Video card does NOT support GL_ARB_framebuffer_object." << std::endl;
    }
#endif

    if(fboSupported)
    {
        // create a MSAA framebuffer object
        // NOTE: All attachment images must have the same # of samples.
        // Ohterwise, the framebuffer status will not be completed.
        glGenFramebuffers(1, &fboMsaaId);
        glBindFramebuffer(GL_FRAMEBUFFER, fboMsaaId);

        // create a MSAA renderbuffer object to store color info
        glGenRenderbuffers(1, &rboColorId);
        glBindRenderbuffer(GL_RENDERBUFFER, rboColorId);
        glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaaCount, GL_RGB8, TEXTURE_WIDTH, TEXTURE_HEIGHT);
        glBindRenderbuffer(GL_RENDERBUFFER, 0);

        // create a MSAA renderbuffer object to store depth info
        // NOTE: A depth renderable image should be attached the FBO for depth test.
        // If we don't attach a depth renderable image to the FBO, then
        // the rendering output will be corrupted because of missing depth test.
        // If you also need stencil test for your rendering, then you must
        // attach additional image to the stencil attachement point, too.
        glGenRenderbuffers(1, &rboDepthId);
        glBindRenderbuffer(GL_RENDERBUFFER, rboDepthId);
        glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaaCount, GL_DEPTH_COMPONENT, TEXTURE_WIDTH, TEXTURE_HEIGHT);
        glBindRenderbuffer(GL_RENDERBUFFER, 0);

        // attach msaa RBOs to FBO attachment points
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rboColorId);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboDepthId);


        // create a normal (no MSAA) FBO to hold a render-to-texture
        glGenFramebuffers(1, &fboId);
        glBindFramebuffer(GL_FRAMEBUFFER, fboId);

        glGenRenderbuffers(1, &rboId);
        glBindRenderbuffer(GL_RENDERBUFFER, rboId);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, TEXTURE_WIDTH, TEXTURE_HEIGHT);
        glBindRenderbuffer(GL_RENDERBUFFER, 0);

        // attach a texture to FBO color attachement point
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureId, 0);

        // attach a rbo to FBO depth attachement point
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboId);

        //@@ disable color buffer if you don't attach any color buffer image,
        //@@ for example, rendering the depth buffer only to a texture.
        //@@ Otherwise, glCheckFramebufferStatus will not be complete.
        //glDrawBuffer(GL_NONE);
        //glReadBuffer(GL_NONE);

        // check FBO status
        printFramebufferInfo(fboMsaaId);
        bool status = checkFramebufferStatus(fboMsaaId);
        if(!status)
            fboUsed = false;

        glBindFramebuffer(GL_FRAMEBUFFER, 0);
    }

    // start timer, the elapsed time will be used for rotating the teapot
    timer.start();

    // the last GLUT call (LOOP)
    // window will be shown and display callback is triggered by events
    // NOTE: this call never return main().
    glutMainLoop(); /* Start GLUT event-processing loop */

    return 0;
}