1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/layers/d3d10/ReadbackManagerD3D10.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,223 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "ReadbackManagerD3D10.h" 1.10 +#include "ReadbackProcessor.h" 1.11 +#include "ReadbackLayer.h" 1.12 + 1.13 +#include "nsIThread.h" 1.14 +#include "nsThreadUtils.h" 1.15 +#include "gfxImageSurface.h" 1.16 +#include "gfxContext.h" 1.17 + 1.18 +namespace mozilla { 1.19 +namespace layers { 1.20 + 1.21 +// Structure that contains the information required to execute a readback task, 1.22 +// the only member accessed off the main thread here is mReadbackTexture. Since 1.23 +// mLayer may be released only on the main thread this object should always be 1.24 +// destroyed on the main thread! 1.25 +struct ReadbackTask { 1.26 + // The texture that we copied the contents of the thebeslayer to. 1.27 + nsRefPtr<ID3D10Texture2D> mReadbackTexture; 1.28 + // This exists purely to keep the ReadbackLayer alive for the lifetime of 1.29 + // mUpdate. Note that this addref and release should occur -solely- on the 1.30 + // main thread. 1.31 + nsRefPtr<ReadbackLayer> mLayer; 1.32 + ReadbackProcessor::Update mUpdate; 1.33 + // The origin in ThebesLayer coordinates of mReadbackTexture. 1.34 + gfxPoint mOrigin; 1.35 + // mLayer->GetBackgroundOffset() when the task is created. We have 1.36 + // to save this in the ReadbackTask because it might change before 1.37 + // the update is delivered to the readback sink. 1.38 + nsIntPoint mBackgroundOffset; 1.39 +}; 1.40 + 1.41 +// This class is created and dispatched from the Readback thread but it must be 1.42 +// destroyed by the main thread. 1.43 +class ReadbackResultWriter MOZ_FINAL : public nsIRunnable 1.44 +{ 1.45 + NS_DECL_THREADSAFE_ISUPPORTS 1.46 +public: 1.47 + ReadbackResultWriter(ReadbackTask *aTask) : mTask(aTask) {} 1.48 + 1.49 + NS_IMETHODIMP Run() 1.50 + { 1.51 + ReadbackProcessor::Update *update = &mTask->mUpdate; 1.52 + 1.53 + if (!update->mLayer->GetSink()) { 1.54 + // This can happen when a plugin is destroyed. 1.55 + return NS_OK; 1.56 + } 1.57 + 1.58 + nsIntPoint offset = mTask->mBackgroundOffset; 1.59 + 1.60 + D3D10_TEXTURE2D_DESC desc; 1.61 + mTask->mReadbackTexture->GetDesc(&desc); 1.62 + 1.63 + D3D10_MAPPED_TEXTURE2D mappedTex; 1.64 + // We know this map will immediately succeed, as we've already mapped this 1.65 + // copied data on our task thread. 1.66 + HRESULT hr = mTask->mReadbackTexture->Map(0, D3D10_MAP_READ, 0, &mappedTex); 1.67 + 1.68 + if (FAILED(hr)) { 1.69 + // If this fails we're never going to get our ThebesLayer content. 1.70 + update->mLayer->GetSink()->SetUnknown(update->mSequenceCounter); 1.71 + return NS_OK; 1.72 + } 1.73 + 1.74 + nsRefPtr<gfxImageSurface> sourceSurface = 1.75 + new gfxImageSurface((unsigned char*)mappedTex.pData, 1.76 + gfxIntSize(desc.Width, desc.Height), 1.77 + mappedTex.RowPitch, 1.78 + gfxImageFormat::RGB24); 1.79 + 1.80 + nsRefPtr<gfxContext> ctx = 1.81 + update->mLayer->GetSink()->BeginUpdate(update->mUpdateRect + offset, 1.82 + update->mSequenceCounter); 1.83 + 1.84 + if (ctx) { 1.85 + ctx->Translate(gfxPoint(offset.x, offset.y)); 1.86 + ctx->SetSource(sourceSurface, gfxPoint(mTask->mOrigin.x, 1.87 + mTask->mOrigin.y)); 1.88 + ctx->Paint(); 1.89 + 1.90 + update->mLayer->GetSink()->EndUpdate(ctx, update->mUpdateRect + offset); 1.91 + } 1.92 + 1.93 + mTask->mReadbackTexture->Unmap(0); 1.94 + 1.95 + return NS_OK; 1.96 + } 1.97 + 1.98 +private: 1.99 + nsAutoPtr<ReadbackTask> mTask; 1.100 +}; 1.101 + 1.102 +NS_IMPL_ISUPPORTS(ReadbackResultWriter, nsIRunnable) 1.103 + 1.104 +DWORD WINAPI StartTaskThread(void *aManager) 1.105 +{ 1.106 + static_cast<ReadbackManagerD3D10*>(aManager)->ProcessTasks(); 1.107 + 1.108 + return 0; 1.109 +} 1.110 + 1.111 +ReadbackManagerD3D10::ReadbackManagerD3D10() 1.112 + : mRefCnt(0) 1.113 +{ 1.114 + ::InitializeCriticalSection(&mTaskMutex); 1.115 + mShutdownEvent = ::CreateEventA(nullptr, FALSE, FALSE, nullptr); 1.116 + mTaskSemaphore = ::CreateSemaphoreA(nullptr, 0, 1000000, nullptr); 1.117 + mTaskThread = ::CreateThread(nullptr, 0, StartTaskThread, this, 0, 0); 1.118 +} 1.119 + 1.120 +ReadbackManagerD3D10::~ReadbackManagerD3D10() 1.121 +{ 1.122 + ::SetEvent(mShutdownEvent); 1.123 + 1.124 + // This shouldn't take longer than 5 seconds, if it does we're going to choose 1.125 + // to leak the thread and its synchronisation in favor of crashing or freezing 1.126 + DWORD result = ::WaitForSingleObject(mTaskThread, 5000); 1.127 + if (result != WAIT_TIMEOUT) { 1.128 + ::DeleteCriticalSection(&mTaskMutex); 1.129 + ::CloseHandle(mShutdownEvent); 1.130 + ::CloseHandle(mTaskSemaphore); 1.131 + ::CloseHandle(mTaskThread); 1.132 + } else { 1.133 + NS_RUNTIMEABORT("ReadbackManager: Task thread did not shutdown in 5 seconds."); 1.134 + } 1.135 +} 1.136 + 1.137 +void 1.138 +ReadbackManagerD3D10::PostTask(ID3D10Texture2D *aTexture, void *aUpdate, const gfxPoint &aOrigin) 1.139 +{ 1.140 + ReadbackTask *task = new ReadbackTask; 1.141 + task->mReadbackTexture = aTexture; 1.142 + task->mUpdate = *static_cast<ReadbackProcessor::Update*>(aUpdate); 1.143 + task->mOrigin = aOrigin; 1.144 + task->mLayer = task->mUpdate.mLayer; 1.145 + task->mBackgroundOffset = task->mLayer->GetBackgroundLayerOffset(); 1.146 + 1.147 + ::EnterCriticalSection(&mTaskMutex); 1.148 + mPendingReadbackTasks.AppendElement(task); 1.149 + ::LeaveCriticalSection(&mTaskMutex); 1.150 + 1.151 + ::ReleaseSemaphore(mTaskSemaphore, 1, nullptr); 1.152 +} 1.153 + 1.154 +HRESULT 1.155 +ReadbackManagerD3D10::QueryInterface(REFIID riid, void **ppvObject) 1.156 +{ 1.157 + if (!ppvObject) { 1.158 + return E_POINTER; 1.159 + } 1.160 + 1.161 + if (riid == IID_IUnknown) { 1.162 + *ppvObject = this; 1.163 + } else { 1.164 + return E_NOINTERFACE; 1.165 + } 1.166 + 1.167 + return S_OK; 1.168 +} 1.169 + 1.170 +ULONG 1.171 +ReadbackManagerD3D10::AddRef() 1.172 +{ 1.173 + NS_ASSERTION(NS_IsMainThread(), 1.174 + "ReadbackManagerD3D10 should only be refcounted on main thread."); 1.175 + return ++mRefCnt; 1.176 +} 1.177 + 1.178 +ULONG 1.179 +ReadbackManagerD3D10::Release() 1.180 +{ 1.181 + NS_ASSERTION(NS_IsMainThread(), 1.182 + "ReadbackManagerD3D10 should only be refcounted on main thread."); 1.183 + ULONG newRefCnt = --mRefCnt; 1.184 + if (!newRefCnt) { 1.185 + mRefCnt++; 1.186 + delete this; 1.187 + } 1.188 + return newRefCnt; 1.189 +} 1.190 + 1.191 +void 1.192 +ReadbackManagerD3D10::ProcessTasks() 1.193 +{ 1.194 + HANDLE handles[] = { mTaskSemaphore, mShutdownEvent }; 1.195 + 1.196 + while (true) { 1.197 + DWORD result = ::WaitForMultipleObjects(2, handles, FALSE, INFINITE); 1.198 + if (result != WAIT_OBJECT_0) { 1.199 + return; 1.200 + } 1.201 + 1.202 + ::EnterCriticalSection(&mTaskMutex); 1.203 + if (mPendingReadbackTasks.Length() == 0) { 1.204 + NS_RUNTIMEABORT("Trying to read from an empty array, bad bad bad"); 1.205 + } 1.206 + ReadbackTask *nextReadbackTask = mPendingReadbackTasks[0].forget(); 1.207 + mPendingReadbackTasks.RemoveElementAt(0); 1.208 + ::LeaveCriticalSection(&mTaskMutex); 1.209 + 1.210 + // We want to block here until the texture contents are available, the 1.211 + // easiest thing is to simply map and unmap. 1.212 + D3D10_MAPPED_TEXTURE2D mappedTex; 1.213 + nextReadbackTask->mReadbackTexture->Map(0, D3D10_MAP_READ, 0, &mappedTex); 1.214 + nextReadbackTask->mReadbackTexture->Unmap(0); 1.215 + 1.216 + // We can only send the update to the sink on the main thread, so post an 1.217 + // event there to do so. Ownership of the task is passed from 1.218 + // mPendingReadbackTasks to ReadbackResultWriter here. 1.219 + nsCOMPtr<nsIThread> thread = do_GetMainThread(); 1.220 + thread->Dispatch(new ReadbackResultWriter(nextReadbackTask), 1.221 + nsIEventTarget::DISPATCH_NORMAL); 1.222 + } 1.223 +} 1.224 + 1.225 +} 1.226 +}