/* * R_BlitTextureToScrFbo */ static void R_BlitTextureToScrFbo( const refdef_t *fd, image_t *image, int dstFbo, int program_type, const vec4_t color, int blendMask ) { int x, y; int w, h; RB_BindFrameBufferObject( dstFbo ); if( !dstFbo ) { // default framebuffer // set the viewport to full resolution // but keep the scissoring region x = fd->x; y = fd->y; w = fd->width; h = fd->height; RB_Viewport( 0, 0, glConfig.width, glConfig.height ); RB_Scissor( rn.scissor[0], rn.scissor[1], rn.scissor[2], rn.scissor[3] ); } else { // aux framebuffer // set the viewport to full resolution of the framebuffer // set scissor to default framebuffer resolution x = 0; y = 0; w = rf.frameBufferWidth; h = rf.frameBufferHeight; RB_Viewport( 0, 0, w, h ); RB_Scissor( 0, 0, glConfig.width, glConfig.height ); } // blit + flip R_DrawStretchQuick( x, y, w, h, (float)(x)/image->upload_width, 1.0 - (float)(y)/image->upload_height, (float)(x+w)/image->upload_width, 1.0 - (float)(y+h)/image->upload_height, color, program_type, image, blendMask ); // restore 2D viewport and scissor RB_Viewport( 0, 0, rf.frameBufferWidth, rf.frameBufferHeight ); RB_Scissor( 0, 0, rf.frameBufferWidth, rf.frameBufferHeight ); }
/* ==================== RB_ExecuteRenderCommands This function will be called syncronously if running without smp extensions, or asyncronously by another thread. ==================== */ void RB_ExecuteRenderCommands( const void *data ) { int t1, t2; t1 = ri.Milliseconds (); if ( !r_smp->integer || data == backEndData[0]->commands.cmds ) { backEnd.smpFrame = 0; } else { backEnd.smpFrame = 1; } while ( 1 ) { switch ( *(const int *)data ) { case RC_SET_COLOR: data = RB_SetColor( data ); break; case RC_STRETCH_PIC: data = RB_StretchPic( data ); break; case RC_ROTATE_PIC: data = RB_RotatePic( data ); break; case RC_ROTATE_PIC2: data = RB_RotatePic2( data ); break; case RC_SCISSOR: data = RB_Scissor( data ); break; case RC_DRAW_SURFS: data = RB_DrawSurfs( data ); break; case RC_DRAW_BUFFER: data = RB_DrawBuffer( data ); break; case RC_SWAP_BUFFERS: data = RB_SwapBuffers( data ); break; case RC_WORLD_EFFECTS: data = RB_WorldEffects( data ); break; case RC_END_OF_LIST: default: // stop rendering on this thread t2 = ri.Milliseconds (); backEnd.pc.msec = t2 - t1; return; } } }
/* * RB_FlushDynamicMeshes */ void RB_FlushDynamicMeshes( void ) { int i, numDraws = rb.numDynamicDraws; rbDynamicStream_t *stream; rbDynamicDraw_t *draw; int sx, sy, sw, sh; float offsetx = 0.0f, offsety = 0.0f, transx, transy; mat4_t m; if( !numDraws ) { return; } for( i = 0; i < RB_VBO_NUM_STREAMS; i++ ) { stream = &rb.dynamicStreams[i]; // R_UploadVBO* are going to rebind buffer arrays for upload // so update our local VBO state cache by calling RB_BindVBO RB_BindVBO( -i - 1, GL_TRIANGLES ); // dummy value for primitive here // because of firstVert, upload elems first if( stream->drawElements.numElems ) { mesh_t elemMesh; memset( &elemMesh, 0, sizeof( elemMesh ) ); elemMesh.elems = dynamicStreamElems[i] + stream->drawElements.firstElem; elemMesh.numElems = stream->drawElements.numElems; R_UploadVBOElemData( stream->vbo, 0, stream->drawElements.firstElem, &elemMesh ); stream->drawElements.firstElem += stream->drawElements.numElems; stream->drawElements.numElems = 0; } if( stream->drawElements.numVerts ) { R_UploadVBOVertexRawData( stream->vbo, stream->drawElements.firstVert, stream->drawElements.numVerts, stream->vertexData + stream->drawElements.firstVert * stream->vbo->vertexSize ); stream->drawElements.firstVert += stream->drawElements.numVerts; stream->drawElements.numVerts = 0; } } RB_GetScissor( &sx, &sy, &sw, &sh ); Matrix4_Copy( rb.objectMatrix, m ); transx = m[12]; transy = m[13]; for( i = 0, draw = rb.dynamicDraws; i < numDraws; i++, draw++ ) { RB_BindShader( draw->entity, draw->shader, draw->fog ); RB_BindVBO( draw->streamId, draw->primitive ); RB_SetPortalSurface( draw->portalSurface ); RB_SetShadowBits( draw->shadowBits ); RB_Scissor( draw->scissor[0], draw->scissor[1], draw->scissor[2], draw->scissor[3] ); // translate the mesh in 2D if( ( offsetx != draw->offset[0] ) || ( offsety != draw->offset[1] ) ) { offsetx = draw->offset[0]; offsety = draw->offset[1]; m[12] = transx + offsetx; m[13] = transy + offsety; RB_LoadObjectMatrix( m ); } RB_DrawElements( draw->drawElements.firstVert, draw->drawElements.numVerts, draw->drawElements.firstElem, draw->drawElements.numElems, draw->drawElements.firstVert, draw->drawElements.numVerts, draw->drawElements.firstElem, draw->drawElements.numElems ); } rb.numDynamicDraws = 0; RB_Scissor( sx, sy, sw, sh ); // restore the original translation in the object matrix if it has been changed if( offsetx || offsety ) { m[12] = transx; m[13] = transy; RB_LoadObjectMatrix( m ); } }
/* * R_BlitTextureToScrFbo */ static void R_BlitTextureToScrFbo( const refdef_t *fd, image_t *image, int dstFbo, int program_type, const vec4_t color, int blendMask, int numShaderImages, image_t **shaderImages ) { int x, y; int w, h, fw, fh; static char s_name[] = "$builtinpostprocessing"; static shaderpass_t p; static shader_t s; int i; static tcmod_t tcmod; mat4_t m; assert( rsh.postProcessingVBO ); // blit + flip using a static mesh to avoid redundant buffer uploads // (also using custom PP effects like FXAA with the stream VBO causes // Adreno to mark the VBO as "slow" (due to some weird bug) // for the rest of the frame and drop FPS to 10-20). RB_FlushDynamicMeshes(); RB_BindFrameBufferObject( dstFbo ); if( !dstFbo ) { // default framebuffer // set the viewport to full resolution // but keep the scissoring region x = fd->x; y = fd->y; w = fw = fd->width; h = fh = fd->height; RB_Viewport( 0, 0, glConfig.width, glConfig.height ); RB_Scissor( rn.scissor[0], rn.scissor[1], rn.scissor[2], rn.scissor[3] ); } else { // aux framebuffer // set the viewport to full resolution of the framebuffer (without the NPOT padding if there's one) // draw quad on the whole framebuffer texture // set scissor to default framebuffer resolution image_t *cb = RFB_GetObjectTextureAttachment( dstFbo, false ); x = 0; y = 0; w = fw = rf.frameBufferWidth; h = fh = rf.frameBufferHeight; if( cb ) { fw = cb->upload_width; fh = cb->upload_height; } RB_Viewport( 0, 0, w, h ); RB_Scissor( 0, 0, glConfig.width, glConfig.height ); } s.vattribs = VATTRIB_POSITION_BIT|VATTRIB_TEXCOORDS_BIT; s.sort = SHADER_SORT_NEAREST; s.numpasses = 1; s.name = s_name; s.passes = &p; p.rgbgen.type = RGB_GEN_IDENTITY; p.alphagen.type = ALPHA_GEN_IDENTITY; p.tcgen = TC_GEN_NONE; p.images[0] = image; for( i = 0; i < numShaderImages; i++ ) p.images[i + 1] = shaderImages[i]; p.flags = blendMask; p.program_type = program_type; if( !dstFbo ) { tcmod.type = TC_MOD_TRANSFORM; tcmod.args[0] = ( float )( w ) / ( float )( image->upload_width ); tcmod.args[1] = ( float )( h ) / ( float )( image->upload_height ); tcmod.args[4] = ( float )( x ) / ( float )( image->upload_width ); tcmod.args[5] = ( float )( image->upload_height - h - y ) / ( float )( image->upload_height ); p.numtcmods = 1; p.tcmods = &tcmod; } else { p.numtcmods = 0; } Matrix4_Identity( m ); Matrix4_Scale2D( m, fw, fh ); Matrix4_Translate2D( m, x, y ); RB_LoadObjectMatrix( m ); RB_BindShader( NULL, &s, NULL ); RB_BindVBO( rsh.postProcessingVBO->index, GL_TRIANGLES ); RB_DrawElements( 0, 4, 0, 6, 0, 0, 0, 0 ); RB_LoadObjectMatrix( mat4x4_identity ); // restore 2D viewport and scissor RB_Viewport( 0, 0, rf.frameBufferWidth, rf.frameBufferHeight ); RB_Scissor( 0, 0, rf.frameBufferWidth, rf.frameBufferHeight ); }
/* * R_ResampleCinematicFrame */ static image_t *R_ResampleCinematicFrame( r_cinhandle_t *handle ) { const int samples = 4; if( !handle->pic ) { // we haven't yet read a new frame, return whatever image we got // this will return NULL until at least one frame has been read return handle->image; } if( handle->yuv ) { int i; if( !handle->yuv_images[0] ) { qbyte *fake_data[1] = { NULL }; const char *letters[3] = { "y", "u", "v" }; for( i = 0; i < 3; i++ ) { handle->yuv_images[i] = R_LoadImage( va( "%s_%s", handle->name, letters[i] ), fake_data, 1, 1, IT_CINEMATIC|IT_LUMINANCE, 1 ); } handle->new_frame = qtrue; } if( handle->new_frame ) { int fbo; qboolean in2D; // render/convert three 8-bit YUV images into RGB framebuffer in2D = rf.in2D; fbo = RFB_BoundObject(); if( !in2D ) { R_PushRefInst(); } R_InitViewportTexture( &handle->image, handle->name, 0, handle->cyuv->image_width, handle->cyuv->image_height, 0, IT_CINEMATIC|IT_FRAMEBUFFER, samples ); R_BindFrameBufferObject( handle->image->fbo ); R_Set2DMode( qtrue ); RB_Scissor( 0, 0, handle->image->upload_width, handle->image->upload_height ); RB_Viewport( 0, 0, handle->image->upload_width, handle->image->upload_height ); // flip the image vertically because we're rendering to a FBO R_DrawStretchRawYUVBuiltin( 0, 0, handle->image->upload_width, handle->image->upload_height, (float)handle->cyuv->x_offset / handle->cyuv->image_width, (float)handle->cyuv->y_offset / handle->cyuv->image_height, (float)(handle->cyuv->x_offset + handle->cyuv->width) / handle->cyuv->image_width, (float)(handle->cyuv->y_offset + handle->cyuv->height) / handle->cyuv->image_height, handle->cyuv->yuv, handle->yuv_images, 2 ); if( !in2D ) { R_PopRefInst( 0 ); } R_BindFrameBufferObject( fbo ); R_Set2DMode( in2D ); handle->new_frame = qfalse; } } else { if( !handle->image ) { handle->image = R_LoadImage( handle->name, &handle->pic, handle->width, handle->height, IT_CINEMATIC, samples ); handle->new_frame = qfalse; } else if( handle->new_frame ) { R_ReplaceImage( handle->image, &handle->pic, handle->width, handle->height, handle->image->flags, samples ); handle->new_frame = qfalse; } } return handle->image; }
/* * R_UploadCinematicFrame */ static void R_UploadCinematicFrame( r_cinhandle_t *handle ) { const int samples = 4; ri.Mutex_Lock( handle->lock ); if( !handle->cin || !handle->pic ) { ri.Mutex_Unlock( handle->lock ); return; } if( handle->yuv ) { int i; if( !handle->yuv_images[0] ) { char tn[256]; uint8_t *fake_data[1] = { NULL }; const char *letters[3] = { "y", "u", "v" }; for( i = 0; i < 3; i++ ) { handle->yuv_images[i] = R_LoadImage( va_r( tn, sizeof( tn ), "%s_%s", handle->name, letters[i] ), fake_data, 1, 1, IT_SPECIAL | IT_NO_DATA_SYNC, 1, IMAGE_TAG_GENERIC, 1 ); } handle->new_frame = true; } if( handle->new_frame ) { bool multiSamples2D; bool in2D; // render/convert three 8-bit YUV images into RGB framebuffer in2D = rf.twoD.enabled; multiSamples2D = rf.twoD.multiSamples; if( in2D ) { R_End2D(); } else { R_PushRefInst(); } R_InitViewportTexture( &handle->image, handle->name, 0, handle->cyuv->image_width, handle->cyuv->image_height, 0, IT_SPECIAL | IT_FRAMEBUFFER, IMAGE_TAG_GENERIC, samples ); R_BindFrameBufferObject( handle->image->fbo ); R_SetupGL2D(); RB_Scissor( 0, 0, handle->image->upload_width, handle->image->upload_height ); RB_Viewport( 0, 0, handle->image->upload_width, handle->image->upload_height ); R_UploadRawYUVPic( handle->yuv_images, handle->cyuv->yuv ); // flip the image vertically because we're rendering to a FBO R_DrawStretchRawYUVBuiltin( 0, 0, handle->image->upload_width, handle->image->upload_height, (float)handle->cyuv->x_offset / handle->cyuv->image_width, (float)handle->cyuv->y_offset / handle->cyuv->image_height, (float)( handle->cyuv->x_offset + handle->cyuv->width ) / handle->cyuv->image_width, (float)( handle->cyuv->y_offset + handle->cyuv->height ) / handle->cyuv->image_height, handle->yuv_images, 2 ); if( in2D ) { R_Begin2D( multiSamples2D ); } else { R_PopRefInst(); } handle->new_frame = false; } } else { if( !handle->image ) { handle->image = R_LoadImage( handle->name, (uint8_t **)&handle->pic, handle->width, handle->height, IT_SPECIAL | IT_NO_DATA_SYNC, 1, IMAGE_TAG_GENERIC, samples ); } if( handle->new_frame ) { R_ReplaceImage( handle->image, (uint8_t **)&handle->pic, handle->width, handle->height, handle->image->flags, 1, samples ); handle->new_frame = false; } } ri.Mutex_Unlock( handle->lock ); }