DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Header

Mercurial (5b81998bb7ab)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "ReadbackManagerD3D10.h"
#include "ReadbackProcessor.h"
#include "ReadbackLayer.h"

#include "nsIThread.h"
#include "nsThreadUtils.h"
#include "gfxImageSurface.h"

namespace mozilla {
namespace layers {

// Structure that contains the information required to execute a readback task,
// the only member accessed off the main thread here is mReadbackTexture. Since
// mLayer may be released only on the main thread this object should always be
// destroyed on the main thread!
struct ReadbackTask {
  // The texture that we copied the contents of the thebeslayer to.
  nsRefPtr<ID3D10Texture2D> mReadbackTexture;
  // This exists purely to keep the ReadbackLayer alive for the lifetime of
  // mUpdate. Note that this addref and release should occur -solely- on the
  // main thread.
  nsRefPtr<ReadbackLayer> mLayer;
  ReadbackProcessor::Update mUpdate;
  // The origin in ThebesLayer coordinates of mReadbackTexture.
  gfxPoint mOrigin;
  // mLayer->GetBackgroundOffset() when the task is created.  We have
  // to save this in the ReadbackTask because it might change before
  // the update is delivered to the readback sink.
  nsIntPoint mBackgroundOffset;
};

// This class is created and dispatched from the Readback thread but it must be
// destroyed by the main thread.
class ReadbackResultWriter MOZ_FINAL : public nsIRunnable
{
  NS_DECL_ISUPPORTS
public:
  ReadbackResultWriter(ReadbackTask *aTask) : mTask(aTask) {}

  NS_IMETHODIMP Run()
  {
    ReadbackProcessor::Update *update = &mTask->mUpdate;

    if (!update->mLayer->GetSink()) {
      // This can happen when a plugin is destroyed.
      return NS_OK;
    }

    nsIntPoint offset = mTask->mBackgroundOffset;

    D3D10_TEXTURE2D_DESC desc;
    mTask->mReadbackTexture->GetDesc(&desc);

    D3D10_MAPPED_TEXTURE2D mappedTex;
    // We know this map will immediately succeed, as we've already mapped this
    // copied data on our task thread.
    HRESULT hr = mTask->mReadbackTexture->Map(0, D3D10_MAP_READ, 0, &mappedTex);

    if (FAILED(hr)) {
      // If this fails we're never going to get our ThebesLayer content.
      update->mLayer->GetSink()->SetUnknown(update->mSequenceCounter);
      return NS_OK;
    }

    nsRefPtr<gfxImageSurface> sourceSurface =
      new gfxImageSurface((unsigned char*)mappedTex.pData,
                          gfxIntSize(desc.Width, desc.Height),
                          mappedTex.RowPitch,
                          gfxASurface::ImageFormatRGB24);

    nsRefPtr<gfxContext> ctx =
      update->mLayer->GetSink()->BeginUpdate(update->mUpdateRect + offset,
                                             update->mSequenceCounter);

    if (ctx) {
      ctx->Translate(gfxPoint(offset.x, offset.y));
      ctx->SetSource(sourceSurface, gfxPoint(mTask->mOrigin.x,
                                             mTask->mOrigin.y));
      ctx->Paint();

      update->mLayer->GetSink()->EndUpdate(ctx, update->mUpdateRect + offset);
    }

    mTask->mReadbackTexture->Unmap(0);

    return NS_OK;
  }

private:
  nsAutoPtr<ReadbackTask> mTask;
};

NS_IMPL_THREADSAFE_ISUPPORTS1(ReadbackResultWriter, nsIRunnable)

DWORD WINAPI StartTaskThread(void *aManager)
{
  static_cast<ReadbackManagerD3D10*>(aManager)->ProcessTasks();

  return 0;
}

ReadbackManagerD3D10::ReadbackManagerD3D10()
  : mRefCnt(0)
{
  ::InitializeCriticalSection(&mTaskMutex);
  mShutdownEvent = ::CreateEventA(NULL, FALSE, FALSE, NULL);
  mTaskSemaphore = ::CreateSemaphoreA(NULL, 0, 1000000, NULL);
  mTaskThread = ::CreateThread(NULL, 0, StartTaskThread, this, 0, 0);
}

ReadbackManagerD3D10::~ReadbackManagerD3D10()
{
  ::SetEvent(mShutdownEvent);

  // This shouldn't take longer than 5 seconds, if it does we're going to choose
  // to leak the thread and its synchronisation in favor of crashing or freezing
  DWORD result = ::WaitForSingleObject(mTaskThread, 5000);
  if (result != WAIT_TIMEOUT) {
    ::DeleteCriticalSection(&mTaskMutex);
    ::CloseHandle(mShutdownEvent);
    ::CloseHandle(mTaskSemaphore);
    ::CloseHandle(mTaskThread);
  } else {
    NS_RUNTIMEABORT("ReadbackManager: Task thread did not shutdown in 5 seconds.");
  }
}

void
ReadbackManagerD3D10::PostTask(ID3D10Texture2D *aTexture, void *aUpdate, const gfxPoint &aOrigin)
{
  ReadbackTask *task = new ReadbackTask;
  task->mReadbackTexture = aTexture;
  task->mUpdate = *static_cast<ReadbackProcessor::Update*>(aUpdate);
  task->mOrigin = aOrigin;
  task->mLayer = task->mUpdate.mLayer;
  task->mBackgroundOffset = task->mLayer->GetBackgroundLayerOffset();

  ::EnterCriticalSection(&mTaskMutex);
  mPendingReadbackTasks.AppendElement(task);
  ::LeaveCriticalSection(&mTaskMutex);

  ::ReleaseSemaphore(mTaskSemaphore, 1, NULL);
}

HRESULT
ReadbackManagerD3D10::QueryInterface(REFIID riid, void **ppvObject)
{
  if (!ppvObject) {
    return E_POINTER;
  }

  if (riid == IID_IUnknown) {
    *ppvObject = this;
  } else {
    return E_NOINTERFACE;
  }

  return S_OK;
}

ULONG
ReadbackManagerD3D10::AddRef()
{
  NS_ASSERTION(NS_IsMainThread(),
    "ReadbackManagerD3D10 should only be refcounted on main thread.");
  return ++mRefCnt;
}

ULONG
ReadbackManagerD3D10::Release()
{
  NS_ASSERTION(NS_IsMainThread(),
    "ReadbackManagerD3D10 should only be refcounted on main thread.");
  ULONG newRefCnt = --mRefCnt;
  if (!newRefCnt) {
    mRefCnt++;
    delete this;
  }
  return newRefCnt;
}

void
ReadbackManagerD3D10::ProcessTasks()
{
  HANDLE handles[] = { mTaskSemaphore, mShutdownEvent };
  
  while (true) {
    DWORD result = ::WaitForMultipleObjects(2, handles, FALSE, INFINITE);
    if (result != WAIT_OBJECT_0) {
      return;
    }

    ::EnterCriticalSection(&mTaskMutex);
    if (mPendingReadbackTasks.Length() == 0) {
      NS_RUNTIMEABORT("Trying to read from an empty array, bad bad bad");
    }
    ReadbackTask *nextReadbackTask = mPendingReadbackTasks[0].forget();
    mPendingReadbackTasks.RemoveElementAt(0);
    ::LeaveCriticalSection(&mTaskMutex);

    // We want to block here until the texture contents are available, the
    // easiest thing is to simply map and unmap.
    D3D10_MAPPED_TEXTURE2D mappedTex;
    nextReadbackTask->mReadbackTexture->Map(0, D3D10_MAP_READ, 0, &mappedTex);
    nextReadbackTask->mReadbackTexture->Unmap(0);

    // We can only send the update to the sink on the main thread, so post an
    // event there to do so. Ownership of the task is passed from
    // mPendingReadbackTasks to ReadbackResultWriter here.
    nsCOMPtr<nsIThread> thread = do_GetMainThread();
    thread->Dispatch(new ReadbackResultWriter(nextReadbackTask),
                     nsIEventTarget::DISPATCH_NORMAL);
  }
}

}
}