PageRenderTime 79ms CodeModel.GetById 15ms app.highlight 59ms RepoModel.GetById 1ms app.codeStats 0ms

/xbmc/cores/VideoRenderers/RenderCapture.cpp

http://github.com/xbmc/xbmc
C++ | 510 lines | 412 code | 66 blank | 32 comment | 84 complexity | 6851f1690cf4140495a627b3a46ea732 MD5 | raw file
  1/*
  2 *      Copyright (C) 2005-2013 Team XBMC
  3 *      http://xbmc.org
  4 *
  5 *  This Program is free software; you can redistribute it and/or modify
  6 *  it under the terms of the GNU General Public License as published by
  7 *  the Free Software Foundation; either version 2, or (at your option)
  8 *  any later version.
  9 *
 10 *  This Program is distributed in the hope that it will be useful,
 11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 13 *  GNU General Public License for more details.
 14 *
 15 *  You should have received a copy of the GNU General Public License
 16 *  along with XBMC; see the file COPYING.  If not, see
 17 *  <http://www.gnu.org/licenses/>.
 18 *
 19 */
 20
 21#include "RenderCapture.h"
 22#include "utils/log.h"
 23#include "windowing/WindowingFactory.h"
 24#include "utils/fastmemcpy.h"
 25#include "settings/AdvancedSettings.h"
 26#include "settings/Settings.h"
 27
 28CRenderCaptureBase::CRenderCaptureBase()
 29{
 30  m_state          = CAPTURESTATE_FAILED;
 31  m_userState      = CAPTURESTATE_FAILED;
 32  m_pixels         = NULL;
 33  m_width          = 0;
 34  m_height         = 0;
 35  m_bufferSize     = 0;
 36  m_flags          = 0;
 37  m_asyncSupported = false;
 38  m_asyncChecked   = false;
 39}
 40
 41CRenderCaptureBase::~CRenderCaptureBase()
 42{
 43}
 44
 45bool CRenderCaptureBase::UseOcclusionQuery()
 46{
 47  if (m_flags & CAPTUREFLAG_IMMEDIATELY)
 48    return false;
 49  else if ((g_advancedSettings.m_videoCaptureUseOcclusionQuery == 0) ||
 50           (g_advancedSettings.m_videoCaptureUseOcclusionQuery == -1 &&
 51            g_Windowing.GetRenderQuirks() & RENDER_QUIRKS_BROKEN_OCCLUSION_QUERY))
 52    return false;
 53  else
 54    return true;
 55}
 56
 57#if defined(TARGET_RASPBERRY_PI)
 58
 59CRenderCaptureDispmanX::CRenderCaptureDispmanX()
 60{
 61}
 62
 63CRenderCaptureDispmanX::~CRenderCaptureDispmanX()
 64{
 65	delete[] m_pixels;
 66}
 67
 68int CRenderCaptureDispmanX::GetCaptureFormat()
 69{
 70	return CAPTUREFORMAT_BGRA;
 71}
 72
 73void CRenderCaptureDispmanX::BeginRender()
 74{
 75}
 76
 77void CRenderCaptureDispmanX::EndRender()
 78{
 79	m_pixels = g_RBP.CaptureDisplay(m_width, m_height, NULL, true);
 80
 81	SetState(CAPTURESTATE_DONE);
 82}
 83
 84void* CRenderCaptureDispmanX::GetRenderBuffer()
 85{
 86    return m_pixels;
 87}
 88
 89void CRenderCaptureDispmanX::ReadOut()
 90{
 91}
 92
 93#elif defined(HAS_GL) || defined(HAS_GLES)
 94
 95CRenderCaptureGL::CRenderCaptureGL()
 96{
 97  m_pbo   = 0;
 98  m_query = 0;
 99  m_occlusionQuerySupported = false;
100}
101
102CRenderCaptureGL::~CRenderCaptureGL()
103{
104#ifndef HAS_GLES
105  if (m_asyncSupported)
106  {
107    if (m_pbo)
108    {
109      glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, m_pbo);
110      glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB);
111      glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);
112      glDeleteBuffersARB(1, &m_pbo);
113    }
114
115    if (m_query)
116      glDeleteQueriesARB(1, &m_query);
117  }
118#endif
119
120  delete[] m_pixels;
121}
122
123int CRenderCaptureGL::GetCaptureFormat()
124{
125  return CAPTUREFORMAT_BGRA;
126}
127
128void CRenderCaptureGL::BeginRender()
129{
130  if (!m_asyncChecked)
131  {
132#ifndef HAS_GLES
133    bool usePbo = CSettings::Get().GetBool("videoplayer.usepbo");
134    m_asyncSupported = g_Windowing.IsExtSupported("GL_ARB_pixel_buffer_object") && usePbo;
135    m_occlusionQuerySupported = g_Windowing.IsExtSupported("GL_ARB_occlusion_query");
136
137    if (m_flags & CAPTUREFLAG_CONTINUOUS)
138    {
139      if (!m_occlusionQuerySupported)
140        CLog::Log(LOGWARNING, "CRenderCaptureGL: GL_ARB_occlusion_query not supported, performance might suffer");
141      if (!g_Windowing.IsExtSupported("GL_ARB_pixel_buffer_object"))
142        CLog::Log(LOGWARNING, "CRenderCaptureGL: GL_ARB_pixel_buffer_object not supported, performance might suffer");
143      if (!usePbo)
144        CLog::Log(LOGWARNING, "CRenderCaptureGL: GL_ARB_pixel_buffer_object disabled, performance might suffer");
145      if (UseOcclusionQuery())
146        CLog::Log(LOGWARNING, "CRenderCaptureGL: GL_ARB_occlusion_query disabled, performance might suffer");
147    }
148#endif
149    m_asyncChecked = true;
150  }
151
152#ifndef HAS_GLES
153  if (m_asyncSupported)
154  {
155    if (!m_pbo)
156      glGenBuffersARB(1, &m_pbo);
157
158    if (UseOcclusionQuery() && m_occlusionQuerySupported)
159    {
160      //generate an occlusion query if we don't have one
161      if (!m_query)
162        glGenQueriesARB(1, &m_query);
163    }
164    else
165    {
166      //don't use an occlusion query, clean up any old one
167      if (m_query)
168      {
169        glDeleteQueriesARB(1, &m_query);
170        m_query = 0;
171      }
172    }
173
174    //start the occlusion query
175    if (m_query)
176      glBeginQueryARB(GL_SAMPLES_PASSED_ARB, m_query);
177
178    //allocate data on the pbo and pixel buffer
179    glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, m_pbo);
180    if (m_bufferSize != m_width * m_height * 4)
181    {
182      m_bufferSize = m_width * m_height * 4;
183      glBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, m_bufferSize, 0, GL_STREAM_READ_ARB);
184      delete[] m_pixels;
185      m_pixels = new uint8_t[m_bufferSize];
186    }
187  }
188  else
189#endif
190  {
191    if (m_bufferSize != m_width * m_height * 4)
192    {
193      delete[] m_pixels;
194      m_bufferSize = m_width * m_height * 4;
195      m_pixels = new uint8_t[m_bufferSize];
196    }
197  }
198}
199
200void CRenderCaptureGL::EndRender()
201{
202#ifndef HAS_GLES
203  if (m_asyncSupported)
204  {
205    glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);
206
207    if (m_query)
208      glEndQueryARB(GL_SAMPLES_PASSED_ARB);
209
210    if (m_flags & CAPTUREFLAG_IMMEDIATELY)
211      PboToBuffer();
212    else
213      SetState(CAPTURESTATE_NEEDSREADOUT);
214  }
215  else
216#endif
217  {
218    SetState(CAPTURESTATE_DONE);
219  }
220}
221
222void* CRenderCaptureGL::GetRenderBuffer()
223{
224#ifndef HAS_GLES
225  if (m_asyncSupported)
226  {
227    return NULL; //offset into the pbo
228  }
229  else
230#endif
231  {
232    return m_pixels;
233  }
234}
235
236void CRenderCaptureGL::ReadOut()
237{
238#ifndef HAS_GLES
239  if (m_asyncSupported)
240  {
241    //we don't care about the occlusion query, we just want to know if the result is available
242    //when it is, the write into the pbo is probably done as well,
243    //so it can be mapped and read without a busy wait
244
245    GLuint readout = 1;
246    if (m_query)
247      glGetQueryObjectuivARB(m_query, GL_QUERY_RESULT_AVAILABLE_ARB, &readout);
248
249    if (readout)
250      PboToBuffer();
251  }
252#endif
253}
254
255void CRenderCaptureGL::PboToBuffer()
256{
257#ifndef HAS_GLES
258  glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, m_pbo);
259  GLvoid* pboPtr = glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB);
260
261  if (pboPtr)
262  {
263    fast_memcpy(m_pixels, pboPtr, m_bufferSize);
264    SetState(CAPTURESTATE_DONE);
265  }
266  else
267  {
268    CLog::Log(LOGERROR, "CRenderCaptureGL::PboToBuffer: glMapBufferARB failed");
269    SetState(CAPTURESTATE_FAILED);
270  }
271
272  glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB);
273  glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0);
274#endif
275}
276
277#elif HAS_DX /*HAS_GL*/
278
279CRenderCaptureDX::CRenderCaptureDX()
280{
281  m_renderSurface = NULL;
282  m_copySurface   = NULL;
283  m_query         = NULL;
284  m_surfaceWidth  = 0;
285  m_surfaceHeight = 0;
286
287  g_Windowing.Register(this);
288}
289
290CRenderCaptureDX::~CRenderCaptureDX()
291{
292  CleanupDX();
293  delete[] m_pixels;
294
295  g_Windowing.Unregister(this);
296}
297
298int CRenderCaptureDX::GetCaptureFormat()
299{
300  return CAPTUREFORMAT_BGRA;
301}
302
303void CRenderCaptureDX::BeginRender()
304{
305  LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice();
306
307  if (!m_asyncChecked)
308  {
309    m_asyncSupported = pD3DDevice->CreateQuery(D3DQUERYTYPE_OCCLUSION, NULL) == D3D_OK;
310    if (m_flags & CAPTUREFLAG_CONTINUOUS)
311    {
312      if (!m_asyncSupported)
313        CLog::Log(LOGWARNING, "CRenderCaptureDX: D3DQUERYTYPE_OCCLUSION not supported, performance might suffer");
314      if (!UseOcclusionQuery())
315        CLog::Log(LOGWARNING, "CRenderCaptureDX: D3DQUERYTYPE_OCCLUSION disabled, performance might suffer");
316    }
317
318    m_asyncChecked = true;
319  }
320
321  HRESULT result;
322
323  if (m_surfaceWidth != m_width || m_surfaceHeight != m_height)
324  {
325    if (m_renderSurface)
326    {
327      while(m_renderSurface->Release() > 0);
328      m_renderSurface = NULL;
329    }
330
331    if (m_copySurface)
332    {
333      while (m_copySurface->Release() > 0);
334      m_copySurface = NULL;
335    }
336
337    result = pD3DDevice->CreateRenderTarget(m_width, m_height, D3DFMT_A8R8G8B8, D3DMULTISAMPLE_NONE, 0, TRUE, &m_renderSurface, NULL);
338    if (result != D3D_OK)
339    {
340      CLog::Log(LOGERROR, "CRenderCaptureDX::BeginRender: CreateRenderTarget failed %s",
341                g_Windowing.GetErrorDescription(result).c_str());
342      SetState(CAPTURESTATE_FAILED);
343      return;
344    }
345
346    result = pD3DDevice->CreateOffscreenPlainSurface(m_width, m_height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &m_copySurface, NULL);
347    if (result != D3D_OK)
348    {
349      CLog::Log(LOGERROR, "CRenderCaptureDX::BeginRender: CreateOffscreenPlainSurface failed %s",
350                g_Windowing.GetErrorDescription(result).c_str());
351      SetState(CAPTURESTATE_FAILED);
352      return;
353    }
354
355    m_surfaceWidth = m_width;
356    m_surfaceHeight = m_height;
357  }
358
359  if (m_bufferSize != m_width * m_height * 4)
360  {
361    m_bufferSize = m_width * m_height * 4;
362    delete[] m_pixels;
363    m_pixels = new uint8_t[m_bufferSize];
364  }
365
366  result = pD3DDevice->SetRenderTarget(0, m_renderSurface);
367  if (result != D3D_OK)
368  {
369    CLog::Log(LOGERROR, "CRenderCaptureDX::BeginRender: SetRenderTarget failed %s",
370              g_Windowing.GetErrorDescription(result).c_str());
371    SetState(CAPTURESTATE_FAILED);
372    return;
373  }
374
375  if (m_asyncSupported && UseOcclusionQuery())
376  {
377    //generate an occlusion query if we don't have one
378    if (!m_query)
379    {
380      result = pD3DDevice->CreateQuery(D3DQUERYTYPE_OCCLUSION, &m_query);
381      if (result != D3D_OK)
382      {
383        CLog::Log(LOGERROR, "CRenderCaptureDX::BeginRender: CreateQuery failed %s",
384                  g_Windowing.GetErrorDescription(result).c_str());
385        m_asyncSupported = false;
386        if (m_query)
387        {
388          while (m_query->Release() > 0);
389          m_query = NULL;
390        }
391      }
392    }
393  }
394  else
395  {
396    //don't use an occlusion query, clean up any old one
397    if (m_query)
398    {
399      while (m_query->Release() > 0);
400      m_query = NULL;
401    }
402  }
403
404  if (m_query)
405    m_query->Issue(D3DISSUE_BEGIN);
406}
407
408void CRenderCaptureDX::EndRender()
409{
410  LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice();
411  //GetRenderTargetData should be async on most drivers,
412  //so the render thread doesn't have to wait for the gpu to copy the data to m_copySurface
413  pD3DDevice->GetRenderTargetData(m_renderSurface, m_copySurface);
414
415  if (m_query)
416  {
417    m_query->Issue(D3DISSUE_END);
418    m_query->GetData(NULL, 0, D3DGETDATA_FLUSH); //flush the query request
419  }
420
421  if (m_flags & CAPTUREFLAG_IMMEDIATELY)
422    SurfaceToBuffer();
423  else
424    SetState(CAPTURESTATE_NEEDSREADOUT);
425}
426
427void CRenderCaptureDX::ReadOut()
428{
429  if (m_query)
430  {
431    //if the result of the occlusion query is available, the data is probably also written into m_copySurface
432    HRESULT result = m_query->GetData(NULL, 0, D3DGETDATA_FLUSH);
433    if (result == S_OK)
434    {
435      SurfaceToBuffer();
436    }
437    else if (result != S_FALSE)
438    {
439      CLog::Log(LOGERROR, "CRenderCaptureDX::ReadOut: GetData failed");
440      SurfaceToBuffer();
441    }
442  }
443  else
444  {
445    SurfaceToBuffer();
446  }
447}
448
449void CRenderCaptureDX::SurfaceToBuffer()
450{
451  D3DLOCKED_RECT lockedRect;
452  if (m_copySurface->LockRect(&lockedRect, NULL, D3DLOCK_READONLY) == D3D_OK)
453  {
454    //if pitch is same, do a direct copy, otherwise copy one line at a time
455    if (lockedRect.Pitch == m_width * 4)
456    {
457      fast_memcpy(m_pixels, lockedRect.pBits, m_width * m_height * 4);
458    }
459    else
460    {
461      for (unsigned int y = 0; y < m_height; y++)
462        fast_memcpy(m_pixels + y * m_width * 4, (uint8_t*)lockedRect.pBits + y * lockedRect.Pitch, m_width * 4);
463    }
464    m_copySurface->UnlockRect();
465    SetState(CAPTURESTATE_DONE);
466  }
467  else
468  {
469    CLog::Log(LOGERROR, "CRenderCaptureDX::SurfaceToBuffer: locking m_copySurface failed");
470    SetState(CAPTURESTATE_FAILED);
471  }
472}
473
474void CRenderCaptureDX::OnLostDevice()
475{
476  CleanupDX();
477  SetState(CAPTURESTATE_FAILED);
478}
479
480void CRenderCaptureDX::OnDestroyDevice()
481{
482  CleanupDX();
483  SetState(CAPTURESTATE_FAILED);
484}
485
486void CRenderCaptureDX::CleanupDX()
487{
488  if (m_renderSurface)
489  {
490    while (m_renderSurface->Release() > 0);
491    m_renderSurface = NULL;
492  }
493
494  if (m_copySurface)
495  {
496    while (m_copySurface->Release() > 0);
497    m_copySurface = NULL;
498  }
499
500  if (m_query)
501  {
502    while (m_query->Release() > 0);
503    m_query = NULL;
504  }
505
506  m_surfaceWidth = 0;
507  m_surfaceHeight = 0;
508}
509
510#endif /*HAS_DX*/