summaryrefslogtreecommitdiffstats
path: root/gfx/layers/TextureSync.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--gfx/layers/TextureSync.cpp283
1 files changed, 283 insertions, 0 deletions
diff --git a/gfx/layers/TextureSync.cpp b/gfx/layers/TextureSync.cpp
new file mode 100644
index 0000000000..6a0b17f206
--- /dev/null
+++ b/gfx/layers/TextureSync.cpp
@@ -0,0 +1,283 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "TextureSync.h"
+
+#include <unordered_set>
+
+#include "base/process_util.h"
+#include "chrome/common/mach_ipc_mac.h"
+#include "mozilla/ipc/SharedMemoryBasic.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/StaticMonitor.h"
+#include "mozilla/StaticPtr.h"
+
+#ifdef DEBUG
+# define LOG_ERROR(str, args...) \
+ PR_BEGIN_MACRO \
+ mozilla::SmprintfPointer msg = mozilla::Smprintf(str, ##args); \
+ NS_WARNING(msg.get()); \
+ PR_END_MACRO
+#else
+# define LOG_ERROR(str, args...) \
+ do { /* nothing */ \
+ } while (0)
+#endif
+
+namespace mozilla {
+
+namespace layers {
+
+// Hold raw pointers and trust that TextureSourceProviders will be
+// unregistered in their destructors - we don't want to keep these
+// alive, and destroying them from the main thread will be an
+// error anyway.
+StaticAutoPtr<nsTArray<TextureSourceProvider*>> gTextureSourceProviders;
+
+static std::map<pid_t, std::unordered_set<uint64_t>> gProcessTextureIds;
+static StaticMonitor gTextureLockMonitor;
+
+const int kSendMessageTimeout = 1000;
+const int kTextureLockTimeout = 32; // We really don't want to wait more than
+ // two frames for a texture to unlock. This
+ // will in any case be very uncommon.
+
+struct WaitForTexturesReply {
+ bool success;
+};
+
+struct WaitForTexturesRequest {
+ pid_t pid;
+};
+
+static std::unordered_set<uint64_t>* GetLockedTextureIdsForProcess(pid_t pid) {
+ gTextureLockMonitor.AssertCurrentThreadOwns();
+
+ if (gProcessTextureIds.find(pid) == gProcessTextureIds.end()) {
+ gProcessTextureIds[pid] = std::unordered_set<uint64_t>();
+ }
+
+ return &gProcessTextureIds.at(pid);
+}
+
+static bool WaitForTextureIdsToUnlock(pid_t pid,
+ const Span<const uint64_t>& textureIds) {
+ {
+ StaticMonitorAutoLock lock(gTextureLockMonitor);
+ std::unordered_set<uint64_t>* freedTextureIds =
+ GetLockedTextureIdsForProcess(pid);
+
+ TimeStamp start = TimeStamp::Now();
+ while (true) {
+ bool allCleared = true;
+ for (uint64_t textureId : textureIds) {
+ if (freedTextureIds->find(textureId) != freedTextureIds->end()) {
+ allCleared = false;
+ }
+ }
+
+ if (allCleared) {
+ return true;
+ }
+
+ if (lock.Wait(TimeDuration::FromMilliseconds(kTextureLockTimeout)) ==
+ CVStatus::Timeout) {
+ return false;
+ }
+
+ // In case the monitor gets signaled multiple times, each less than
+ // kTextureLockTimeout. This ensures that the total time we wait is
+ // < 2 * kTextureLockTimeout
+ if ((TimeStamp::Now() - start).ToMilliseconds() >
+ (double)kTextureLockTimeout) {
+ return false;
+ }
+ }
+ }
+}
+
+static void CheckTexturesForUnlock() {
+ if (gTextureSourceProviders) {
+ for (auto it = gTextureSourceProviders->begin();
+ it != gTextureSourceProviders->end(); ++it) {
+ (*it)->TryUnlockTextures();
+ }
+ }
+}
+
+void TextureSync::DispatchCheckTexturesForUnlock() {
+ RefPtr<Runnable> task =
+ NS_NewRunnableFunction("CheckTexturesForUnlock", &CheckTexturesForUnlock);
+ CompositorThread()->Dispatch(task.forget());
+}
+
+void TextureSync::HandleWaitForTexturesMessage(MachReceiveMessage* rmsg,
+ ipc::MemoryPorts* ports) {
+ WaitForTexturesRequest* req =
+ reinterpret_cast<WaitForTexturesRequest*>(rmsg->GetData());
+ uint64_t* textureIds = (uint64_t*)(req + 1);
+ uint32_t textureIdsLength =
+ (rmsg->GetDataLength() - sizeof(WaitForTexturesRequest)) /
+ sizeof(uint64_t);
+
+ bool success =
+ WaitForTextureIdsToUnlock(req->pid, Span(textureIds, textureIdsLength));
+
+ if (!success) {
+ LOG_ERROR("Waiting for textures to unlock failed.\n");
+ }
+
+ MachSendMessage msg(ipc::kReturnWaitForTexturesMsg);
+ WaitForTexturesReply replydata;
+ replydata.success = success;
+ msg.SetData(&replydata, sizeof(WaitForTexturesReply));
+ kern_return_t err = ports->mSender->SendMessage(msg, kSendMessageTimeout);
+ if (KERN_SUCCESS != err) {
+ LOG_ERROR("SendMessage failed 0x%x %s\n", err, mach_error_string(err));
+ }
+}
+
+void TextureSync::RegisterTextureSourceProvider(
+ TextureSourceProvider* textureSourceProvider) {
+ if (!gTextureSourceProviders) {
+ gTextureSourceProviders = new nsTArray<TextureSourceProvider*>();
+ }
+ MOZ_RELEASE_ASSERT(!gTextureSourceProviders->Contains(textureSourceProvider));
+ gTextureSourceProviders->AppendElement(textureSourceProvider);
+}
+
+void TextureSync::UnregisterTextureSourceProvider(
+ TextureSourceProvider* textureSourceProvider) {
+ if (gTextureSourceProviders) {
+ MOZ_ASSERT(gTextureSourceProviders->Contains(textureSourceProvider));
+ gTextureSourceProviders->RemoveElement(textureSourceProvider);
+ if (gTextureSourceProviders->Length() == 0) {
+ gTextureSourceProviders = nullptr;
+ }
+ }
+}
+
+void TextureSync::SetTexturesLocked(pid_t pid,
+ const nsTArray<uint64_t>& textureIds) {
+ StaticMonitorAutoLock mal(gTextureLockMonitor);
+ std::unordered_set<uint64_t>* lockedTextureIds =
+ GetLockedTextureIdsForProcess(pid);
+ for (uint64_t textureId : textureIds) {
+ lockedTextureIds->insert(textureId);
+ }
+}
+
+void TextureSync::SetTexturesUnlocked(pid_t pid,
+ const nsTArray<uint64_t>& textureIds) {
+ bool oneErased = false;
+ {
+ StaticMonitorAutoLock mal(gTextureLockMonitor);
+ std::unordered_set<uint64_t>* lockedTextureIds =
+ GetLockedTextureIdsForProcess(pid);
+ for (uint64_t textureId : textureIds) {
+ if (lockedTextureIds->erase(textureId)) {
+ oneErased = true;
+ }
+ }
+ }
+ if (oneErased) {
+ gTextureLockMonitor.NotifyAll();
+ }
+}
+
+void TextureSync::Shutdown() {
+ {
+ StaticMonitorAutoLock lock(gTextureLockMonitor);
+
+ for (auto& lockedTextureIds : gProcessTextureIds) {
+ lockedTextureIds.second.clear();
+ }
+ }
+
+ gTextureLockMonitor.NotifyAll();
+
+ {
+ StaticMonitorAutoLock lock(gTextureLockMonitor);
+ gProcessTextureIds.clear();
+ }
+}
+
+void TextureSync::UpdateTextureLocks(base::ProcessId aProcessId) {
+ if (aProcessId == base::GetCurrentProcId()) {
+ DispatchCheckTexturesForUnlock();
+ return;
+ }
+
+ MachSendMessage smsg(ipc::kUpdateTextureLocksMsg);
+ smsg.SetData(&aProcessId, sizeof(aProcessId));
+ ipc::SharedMemoryBasic::SendMachMessage(aProcessId, smsg, NULL);
+}
+
+bool TextureSync::WaitForTextures(base::ProcessId aProcessId,
+ const nsTArray<uint64_t>& textureIds) {
+ if (aProcessId == base::GetCurrentProcId()) {
+ bool success = WaitForTextureIdsToUnlock(aProcessId, Span(textureIds));
+ if (!success) {
+ LOG_ERROR("Failed waiting for textures to unlock.\n");
+ }
+
+ return success;
+ }
+
+ MachSendMessage smsg(ipc::kWaitForTexturesMsg);
+ size_t messageSize =
+ sizeof(WaitForTexturesRequest) + textureIds.Length() * sizeof(uint64_t);
+ UniquePtr<uint8_t[]> messageData = MakeUnique<uint8_t[]>(messageSize);
+ WaitForTexturesRequest* req = (WaitForTexturesRequest*)messageData.get();
+ uint64_t* reqTextureIds = (uint64_t*)(req + 1);
+
+ for (uint32_t i = 0; i < textureIds.Length(); ++i) {
+ reqTextureIds[i] = textureIds[i];
+ }
+
+ req->pid = base::GetCurrentProcId();
+ bool dataWasSet = smsg.SetData(req, messageSize);
+
+ if (!dataWasSet) {
+ LOG_ERROR("Data was too large: %zu\n", messageSize);
+ return false;
+ }
+
+ MachReceiveMessage msg;
+ bool success =
+ ipc::SharedMemoryBasic::SendMachMessage(aProcessId, smsg, &msg);
+ if (!success) {
+ return false;
+ }
+
+ if (msg.GetDataLength() != sizeof(WaitForTexturesReply)) {
+ LOG_ERROR("Improperly formatted reply\n");
+ return false;
+ }
+
+ WaitForTexturesReply* msg_data =
+ reinterpret_cast<WaitForTexturesReply*>(msg.GetData());
+ if (!msg_data->success) {
+ LOG_ERROR("Failed waiting for textures to unlock.\n");
+ return false;
+ }
+
+ return true;
+}
+
+void TextureSync::CleanupForPid(base::ProcessId aProcessId) {
+ {
+ StaticMonitorAutoLock lock(gTextureLockMonitor);
+ std::unordered_set<uint64_t>* lockedTextureIds =
+ GetLockedTextureIdsForProcess(aProcessId);
+ lockedTextureIds->clear();
+ }
+ gTextureLockMonitor.NotifyAll();
+}
+
+} // namespace layers
+
+} // namespace mozilla