Ejemplo n.º 1
0
/**
 * Captures the frame in BGRA format from the backbuffer.  This method works but is really slow because it locks the backbuffer
 * during the copy and prevents any furthur rendering until the copy is complete.  It also locks the backbuffer surface
 * immediately after requesting the render target data which is bad because it doesn't give the GPU time to prepare the data
 * for locking asynchronously.
 */
bool CaptureFrame_Slow(int captureWidth, int captureHeight, unsigned char*& outBgraFrame)
{
    bool grabbed = false;

    // Grab the back buffer
    IDirect3DSurface9* pBackBuffer = nullptr;
    gGraphicsDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer);

    // Backbuffer not available
    if (pBackBuffer == nullptr)
    {
        return false;
    }

    // Get backbuffer info
    D3DSURFACE_DESC srcDesc;
    pBackBuffer->GetDesc(&srcDesc);

    // Create/recreate the target textures and surfaces if needed
    if (gResizeTexture == nullptr || captureHeight != gBroadcastHeight || captureWidth != gBroadcastWidth)
    {
        SAFE_RELEASE(gCaptureSurface);
        SAFE_RELEASE(gResizeSurface);
        SAFE_RELEASE(gResizeTexture);

        gBroadcastHeight = captureHeight;
        gBroadcastWidth = captureWidth;

        // Allocate a texture for the stretch and copy
        if ( FAILED(gGraphicsDevice->CreateTexture(captureWidth, captureHeight, 1, D3DUSAGE_RENDERTARGET, srcDesc.Format, D3DPOOL_DEFAULT, &gResizeTexture, nullptr)) )
        {
            ReportError("Error allocating resize texture");
            return false;
        }

        if ( FAILED(gResizeTexture->GetSurfaceLevel(0, &gResizeSurface)) )
        {
            ReportError("Error retrieving surface");
            return false;
        }

        // Allocate a surface for capturing the final buffer
        if ( FAILED(gGraphicsDevice->CreateOffscreenPlainSurface(captureWidth, captureHeight, srcDesc.Format, D3DPOOL_SYSTEMMEM, &gCaptureSurface, nullptr)) )
        {
            ReportError("Error allocating capture surface");
            return false;
        }

        // Clear the surface to black
        if ( FAILED(gGraphicsDevice->ColorFill(gResizeSurface, nullptr, D3DCOLOR_ARGB(0, 0, 0, 0))) )
        {
            ReportError("Error filling with black");
            return false;
        }
    }

    // Stretch and copy the image to the correct area of the destination (black-bordering if necessary)
    float captureAspect = (float)captureHeight / (float)captureWidth;
    float srcAspect = (float)srcDesc.Height / (float)srcDesc.Width;
    RECT destRect;

    // Determine the destination rectangle
    if (captureAspect >= srcAspect)
    {
        float scale = (float)captureWidth / (float)srcDesc.Width;

        destRect.left = 0;
        destRect.right = captureWidth-1;
        destRect.top = (LONG)( ((float)captureHeight - (float)srcDesc.Height*scale) / 2 );
        destRect.bottom = (LONG)( ((float)captureHeight + (float)srcDesc.Height*scale) / 2 );
    }
    else
    {
        float scale = (float)captureHeight / (float)srcDesc.Height;

        destRect.top = 0;
        destRect.bottom = captureHeight-1;
        destRect.left = (LONG)( ((float)captureWidth - (float)srcDesc.Width*scale) / 2 );
        destRect.right = (LONG)( ((float)captureWidth + (float)srcDesc.Width*scale) / 2 );
    }

    // Do the stretch and copy
    if ( FAILED(gGraphicsDevice->StretchRect(pBackBuffer, nullptr, gResizeSurface, &destRect, D3DTEXF_LINEAR)) )
    {
        ReportError("Error in StretchRect");
        return false;
    }

    // Capture the results of the stretch and copy
    gGraphicsDevice->GetRenderTargetData(gResizeSurface, gCaptureSurface);

    // This is expensive since LockRect will block until GetRenderTargetData finishes
    D3DLOCKED_RECT rect;
    if ( FAILED(gCaptureSurface->LockRect(&rect, 0, D3DLOCK_READONLY)) )
    {
        ReportError("Error locking rectangle");
        gCaptureSurface->UnlockRect();
        return false;
    }

    const int kPixelSize = 4;
    const int bgraWidthBytes = captureWidth * kPixelSize;
    const int bgraFrameBytes = captureWidth * captureHeight * kPixelSize;

    // Grab the free buffer from the streaming pool
    outBgraFrame = GetNextFreeBuffer();

    // Copy the buffer
    if (outBgraFrame != nullptr)
    {
        for (int y = 0; y < captureHeight; ++y)
        {
            memcpy( outBgraFrame + y * bgraWidthBytes, (char*)rect.pBits +  y * bgraWidthBytes, bgraWidthBytes );
        }
        grabbed = true;
    }

    gCaptureSurface->UnlockRect();
    pBackBuffer->Release();

    return grabbed;
}
Ejemplo n.º 2
0
/**
 * Captures the frame in RGBA format from the backbuffer.  This method works but is really slow because it locks the backbuffer
 * during the copy and prevents any furthur rendering until the copy is complete.  It also locks the backbuffer surface 
 * immediately after requesting the render target data which is bad because it doesn't give the GPU time to prepare the data
 * for locking asynchronously.
 */
bool CaptureFrame_Slow(int captureWidth, int captureHeight, unsigned char*& outRgbaFrame)
{
	// Create/recreate the target textures and surfaces if needed
	if (gResizeFBO == 0 || captureHeight != gBroadcastHeight || captureWidth != gBroadcastWidth)		
	{
		// recreate resize FBO
		CreateResizeFBO(captureWidth, captureHeight);

		// recreate capture PBO
		CreateCapturePBO(captureWidth, captureHeight);

		gBroadcastHeight = captureHeight;
		gBroadcastWidth = captureWidth;
	}

	// strect to resize FBO
	RenderToResizeFBO(captureWidth, captureHeight);

	// read resize FBO to capture PBO
	{
		glBindFramebuffer(GL_FRAMEBUFFER, gResizeFBO);

		glBindBuffer(GL_PIXEL_PACK_BUFFER, gCapturePBO);
	
		glReadPixels(0, 0, captureWidth, captureHeight, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);

		glBindFramebuffer(GL_FRAMEBUFFER, 0);
	}	

	// lock PBO for reading
	void* buffer = glMapBuffer(GL_PIXEL_PACK_BUFFER , GL_READ_ONLY);
	if ( buffer == nullptr )
	{
		ReportError("Error locking PBO");
		return false;
	}

	const int kPixelSize = 4;
	const int rgbaWidthBytes = captureWidth * kPixelSize;
	const int bgraFrameBytes = captureWidth * captureHeight * kPixelSize;

	// Grab the free buffer from the streaming pool
	outRgbaFrame = GetNextFreeBuffer();

	if ( outRgbaFrame != nullptr )
	{
		memcpy(outRgbaFrame, buffer, bgraFrameBytes);
	}
	
	GLboolean success;
	success = glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
	if ( !success )
	{
		ReportError("Error unlocking PBO");
		glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
		return false;
	}

	glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);

	return true;
}