Import Cobalt 6.15306
Change-Id: I401726588e8df7b26ef2af62f94a4ab270663b94
diff --git a/src/cobalt/audio/audio_file_reader_wav.cc b/src/cobalt/audio/audio_file_reader_wav.cc
index 4edc13f..d0875f2 100644
--- a/src/cobalt/audio/audio_file_reader_wav.cc
+++ b/src/cobalt/audio/audio_file_reader_wav.cc
@@ -153,6 +153,10 @@
// Load channel count.
number_of_channels_ = load_uint16_little_endian(data + offset + 2);
+ if (number_of_channels_ == 0) {
+ DLOG(ERROR) << "No channel on WAV.";
+ return false;
+ }
// Load sample rate.
sample_rate_ =
diff --git a/src/cobalt/base/base.gyp b/src/cobalt/base/base.gyp
index 19652ec..29ad3e2 100644
--- a/src/cobalt/base/base.gyp
+++ b/src/cobalt/base/base.gyp
@@ -49,7 +49,6 @@
'poller.h',
'polymorphic_downcast.h',
'polymorphic_equatable.h',
- 'sanitizer_options.cc',
'source_location.h',
'stop_watch.cc',
'stop_watch.h',
diff --git a/src/cobalt/base/sanitizer_options.cc b/src/cobalt/base/sanitizer_options.cc
deleted file mode 100644
index b5311a5..0000000
--- a/src/cobalt/base/sanitizer_options.cc
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015 Google Inc. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#if defined(ADDRESS_SANITIZER)
-
-// Functions returning default options are declared weak in the tools' runtime
-// libraries. To make the linker pick the strong replacements for those
-// functions from this module, we explicitly force its inclusion by passing
-// -Wl,-u_sanitizer_options_link_helper
-extern "C" void _sanitizer_options_link_helper() { }
-
-// The callbacks we define here will be called from the sanitizer runtime, but
-// aren't referenced from the executable. We must ensure that those
-// callbacks are not sanitizer-instrumented, and that they aren't stripped by
-// the linker.
-#define SANITIZER_HOOK_ATTRIBUTE \
- extern "C" \
- __attribute__((no_sanitize_address)) \
- __attribute__((no_sanitize_memory)) \
- __attribute__((no_sanitize_thread)) \
- __attribute__((visibility("default"))) \
- __attribute__((used))
-
-extern "C" char kLSanDefaultSuppressions[];
-
-char kLSanDefaultSuppressions[] =
- // 16-byte leak from a call to calloc() in this library.
- "leak:egl_gallium.so\n";
-
-SANITIZER_HOOK_ATTRIBUTE const char *__lsan_default_suppressions() {
- return kLSanDefaultSuppressions;
-}
-
-#endif // defined(ADDRESS_SANITIZER)
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index 170907c..ffded5e 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -51,6 +51,8 @@
#include "lbshell/src/lb_memory_pages.h"
#endif // defined(__LB_SHELL__)
#if defined(OS_STARBOARD)
+#include "nb/analytics/memory_tracker.h"
+#include "nb/analytics/memory_tracker_impl.h"
#include "starboard/configuration.h"
#include "starboard/log.h"
#endif // defined(OS_STARBOARD)
@@ -110,6 +112,19 @@
}
return webdriver_port;
}
+
+std::string GetWebDriverListenIp() {
+ // The default port on which the webdriver server should listen for incoming
+ // connections.
+ std::string webdriver_listen_ip =
+ webdriver::WebDriverModule::kDefaultListenIp;
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ if (command_line->HasSwitch(switches::kWebDriverListenIp)) {
+ webdriver_listen_ip =
+ command_line->GetSwitchValueASCII(switches::kWebDriverListenIp);
+ }
+ return webdriver_listen_ip;
+}
#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
#endif // ENABLE_WEBDRIVER
@@ -297,6 +312,14 @@
start_time_(base::TimeTicks::Now()),
stats_update_timer_(true, true),
lite_stats_update_timer_(true, true) {
+ // Check to see if a timed_trace has been set, indicating that we should
+ // begin a timed trace upon startup.
+ base::TimeDelta trace_duration = GetTimedTraceDuration();
+ if (trace_duration != base::TimeDelta()) {
+ trace_event::TraceToFileForDuration(
+ FilePath(FILE_PATH_LITERAL("timed_trace.json")), trace_duration);
+ }
+
DCHECK(MessageLoop::current());
DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type());
@@ -316,14 +339,6 @@
base::Bind(&Application::UpdatePeriodicLiteStats,
base::Unretained(this)));
- // Check to see if a timed_trace has been set, indicating that we should
- // begin a timed trace upon startup.
- base::TimeDelta trace_duration = GetTimedTraceDuration();
- if (trace_duration != base::TimeDelta()) {
- trace_event::TraceToFileForDuration(
- FilePath(FILE_PATH_LITERAL("timed_trace.json")), trace_duration);
- }
-
// Get the initial URL.
GURL initial_url = GetInitialURL();
DLOG(INFO) << "Initial URL: " << initial_url;
@@ -419,6 +434,20 @@
DLOG(INFO) << "Use ShellRawVideoDecoderStub";
options.media_module_options.use_video_decoder_stub = true;
}
+ if (command_line->HasSwitch(switches::kMemoryTracker)) {
+#if defined(OS_STARBOARD)
+ using nb::analytics::MemoryTrackerPrintThread;
+ using nb::analytics::MemoryTracker;
+
+ DLOG(INFO) << "Using MemoryTracking";
+ MemoryTracker* memory_tracker = MemoryTracker::Get();
+ memory_tracker->InstallGlobalTrackingHooks();
+ memory_tracker_print_thread_ = CreateDebugPrintThread(memory_tracker);
+#else
+ DLOG(INFO)
+ << "Memory tracker is not enabled on non-starboard builds.";
+#endif
+ }
#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
base::optional<math::Size> viewport_size;
@@ -483,10 +512,10 @@
#if defined(ENABLE_WEBDRIVER)
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
if (command_line->HasSwitch(switches::kEnableWebDriver)) {
- int webdriver_port = GetWebDriverPort();
web_driver_module_.reset(new webdriver::WebDriverModule(
- webdriver_port, base::Bind(&BrowserModule::CreateSessionDriver,
- base::Unretained(browser_module_.get())),
+ GetWebDriverPort(), GetWebDriverListenIp(),
+ base::Bind(&BrowserModule::CreateSessionDriver,
+ base::Unretained(browser_module_.get())),
base::Bind(&BrowserModule::RequestScreenshotToBuffer,
base::Unretained(browser_module_.get())),
base::Bind(&BrowserModule::SetProxy,
@@ -521,6 +550,13 @@
}
Application::~Application() {
+#if defined(OS_STARBOARD)
+ // explicitly reset here because the destruction of the object is complex
+ // and involves a thread join. If this were to hang the app then having
+ // the destruction at this point gives a real file-line number and a place
+ // for the debugger to land.
+ memory_tracker_print_thread_.reset(NULL);
+#endif
// Unregister event callbacks.
event_dispatcher_.RemoveEventCallback(account::AccountEvent::TypeId(),
account_event_callback_);
diff --git a/src/cobalt/browser/application.h b/src/cobalt/browser/application.h
index da9f8b1..a118a1c 100644
--- a/src/cobalt/browser/application.h
+++ b/src/cobalt/browser/application.h
@@ -33,6 +33,11 @@
#include "cobalt/debug/debug_web_server.h"
#endif
+#if defined(OS_STARBOARD)
+#include "nb/analytics/memory_tracker.h"
+#include "nb/scoped_ptr.h"
+#endif
+
namespace cobalt {
namespace browser {
@@ -173,6 +178,13 @@
base::Timer stats_update_timer_;
base::Timer lite_stats_update_timer_;
+
+#if defined(OS_STARBOARD)
+ // This thread (when active) will print out memory statistics of the engine.
+ // It is activated by the command line -memory_tracker.
+ nb::scoped_ptr<nb::analytics::MemoryTrackerPrintThread>
+ memory_tracker_print_thread_;
+#endif
};
// Factory method for creating an application. It should be implemented
diff --git a/src/cobalt/browser/browser.gyp b/src/cobalt/browser/browser.gyp
index 1115bbd..3572dbb 100644
--- a/src/cobalt/browser/browser.gyp
+++ b/src/cobalt/browser/browser.gyp
@@ -75,6 +75,7 @@
'<(DEPTH)/cobalt/webdriver/webdriver.gyp:webdriver',
'<(DEPTH)/cobalt/xhr/xhr.gyp:xhr',
'<(DEPTH)/googleurl/googleurl.gyp:googleurl',
+ '<(DEPTH)/nb/nb.gyp:nb',
'browser_bindings.gyp:bindings',
'screen_shot_writer',
],
@@ -82,11 +83,6 @@
['enable_about_scheme == 1', {
'defines': [ 'ENABLE_ABOUT_SCHEME' ],
}],
- ['OS=="starboard" or (OS=="lb_shell" and target_arch == "ps3")', {
- 'dependencies': [
- '<(DEPTH)/nb/nb.gyp:nb',
- ],
- }],
],
},
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index 0911d57..e725572 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -37,6 +37,7 @@
#include "cobalt/dom/keycode.h"
#include "cobalt/h5vcc/h5vcc.h"
#include "cobalt/input/input_device_manager_fuzzer.h"
+#include "nb/memory_scope.h"
namespace cobalt {
namespace browser {
@@ -135,10 +136,6 @@
renderer_module_.pipeline()->GetResourceProvider())),
array_buffer_cache_(new dom::ArrayBuffer::Cache(3 * 1024 * 1024)),
#endif // defined(ENABLE_GPU_ARRAY_BUFFER_ALLOCATOR)
- media_module_(media::MediaModule::Create(
- system_window, renderer_module_.render_target()->GetSize(),
- renderer_module_.pipeline()->GetResourceProvider(),
- options.media_module_options)),
network_module_(&storage_manager_, system_window->event_dispatcher(),
options.network_module_options),
render_tree_combiner_(&renderer_module_,
@@ -171,6 +168,15 @@
has_resumed_(true, false),
will_quit_(false),
suspended_(false) {
+ // All allocations for media will be tracked by "Media" memory scope.
+ {
+ TRACK_MEMORY_SCOPE("Media");
+ media_module_ = (media::MediaModule::Create(
+ system_window, renderer_module_.render_target()->GetSize(),
+ renderer_module_.pipeline()->GetResourceProvider(),
+ options.media_module_options));
+ }
+
// Setup our main web module to have the H5VCC API injected into it.
DCHECK(!ContainsKey(web_module_options_.injected_window_attributes, "h5vcc"));
h5vcc::H5vcc::Settings h5vcc_settings;
@@ -258,6 +264,15 @@
renderer_module_.pipeline()->GetResourceProvider(),
kLayoutMaxRefreshFrequencyInHz));
+#if defined(STARBOARD)
+#if SB_HAS(1_CORE)
+ // Wait until the splash screen is ready before loading the main web module.
+ // This prevents starvation of the splash screen module and decoding of the
+ // splash screen image(s).
+ splash_screen_->WaitUntilReady();
+#endif
+#endif
+
// Create new WebModule.
#if !defined(COBALT_FORCE_CSP)
web_module_options_.csp_insecure_allowed_token =
diff --git a/src/cobalt/browser/splash_screen.cc b/src/cobalt/browser/splash_screen.cc
index a2a3d97..0ccc88c 100644
--- a/src/cobalt/browser/splash_screen.cc
+++ b/src/cobalt/browser/splash_screen.cc
@@ -30,14 +30,17 @@
render_tree_produced_callback,
network::NetworkModule* network_module, const math::Size& window_dimensions,
render_tree::ResourceProvider* resource_provider, float layout_refresh_rate,
- const SplashScreen::Options& options) {
+ const SplashScreen::Options& options)
+ : render_tree_produced_callback_(render_tree_produced_callback)
+ , is_ready_(true, false) {
WebModule::Options web_module_options;
web_module_options.name = "SplashScreenWebModule";
web_module_.reset(new WebModule(
- options.url, render_tree_produced_callback,
+ options.url,
+ base::Bind(&SplashScreen::OnRenderTreeProduced, base::Unretained(this)),
base::Bind(&SplashScreen::OnError, base::Unretained(this)),
- base::Closure(), /* window_close_callback */
+ base::Bind(&SplashScreen::OnWindowClosed, base::Unretained(this)),
&stub_media_module_, network_module, window_dimensions, resource_provider,
layout_refresh_rate, web_module_options));
}
@@ -47,5 +50,19 @@
web_module_->Resume(resource_provider);
}
+void SplashScreen::WaitUntilReady() {
+ is_ready_.Wait();
+}
+
+void SplashScreen::OnRenderTreeProduced(
+ const browser::WebModule::LayoutResults& layout_results) {
+ is_ready_.Signal();
+ render_tree_produced_callback_.Run(layout_results);
+}
+
+void SplashScreen::OnWindowClosed() {
+ is_ready_.Signal();
+}
+
} // namespace browser
} // namespace cobalt
diff --git a/src/cobalt/browser/splash_screen.h b/src/cobalt/browser/splash_screen.h
index a105fe4..7888fa8 100644
--- a/src/cobalt/browser/splash_screen.h
+++ b/src/cobalt/browser/splash_screen.h
@@ -21,6 +21,7 @@
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/waitable_event.h"
#include "cobalt/browser/web_module.h"
#include "cobalt/media/media_module_stub.h"
#include "googleurl/src/gurl.h"
@@ -48,13 +49,28 @@
void Suspend();
void Resume(render_tree::ResourceProvider* resource_provider);
+ // Block the caller until the splash screen is ready to be rendered.
+ void WaitUntilReady();
+
private:
+ void OnRenderTreeProduced(
+ const browser::WebModule::LayoutResults& layout_results);
+
void OnError(const GURL& /* url */, const std::string& error) {
+ is_ready_.Signal();
LOG(ERROR) << error;
}
+ void OnWindowClosed();
+
media::MediaModuleStub stub_media_module_;
scoped_ptr<WebModule> web_module_;
+
+ WebModule::OnRenderTreeProducedCallback render_tree_produced_callback_;
+
+ // Signalled once the splash screen has produced its first render tree or
+ // an error occurred.
+ base::WaitableEvent is_ready_;
};
} // namespace browser
diff --git a/src/cobalt/browser/stack_size_constants.h b/src/cobalt/browser/stack_size_constants.h
new file mode 100644
index 0000000..d700f4b
--- /dev/null
+++ b/src/cobalt/browser/stack_size_constants.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_BROWSER_STACK_SIZE_CONSTANTS_H_
+#define COBALT_BROWSER_STACK_SIZE_CONSTANTS_H_
+
+#include "cobalt/base/address_sanitizer.h"
+
+namespace cobalt {
+namespace browser {
+#if defined(COBALT_BUILD_TYPE_DEBUG)
+ // Non-optimized builds require a bigger stack size.
+ const size_t kBaseStackSize = 2 * 1024 * 1024;
+#else
+ const size_t kBaseStackSize = 256 * 1024;
+#endif
+ const size_t kWebModuleStackSize =
+ kBaseStackSize + base::kAsanAdditionalStackSize;
+} // namespace browser
+} // namespace cobalt
+
+#endif // COBALT_BROWSER_STACK_SIZE_CONSTANTS_H_
diff --git a/src/cobalt/browser/switches.cc b/src/cobalt/browser/switches.cc
index 6332a9d..91e0e6d 100644
--- a/src/cobalt/browser/switches.cc
+++ b/src/cobalt/browser/switches.cc
@@ -101,6 +101,13 @@
// Port that the WebDriver server should be listening on.
const char kWebDriverPort[] = "webdriver_port";
+// IP that the WebDriver server should be listening on.
+// (INADDR_ANY if unspecified).
+const char kWebDriverListenIp[] = "webdriver_listen_ip";
+
+// Enables memory tracking by installing the memory tracker on startup.
+const char kMemoryTracker[] = "memory_tracker";
+
#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
// Determines the capacity of the image cache which manages image surfaces
diff --git a/src/cobalt/browser/switches.h b/src/cobalt/browser/switches.h
index a9b3d5f..7ef6a28 100644
--- a/src/cobalt/browser/switches.h
+++ b/src/cobalt/browser/switches.h
@@ -44,6 +44,8 @@
extern const char kVideoContainerSizeOverride[];
extern const char kVideoDecoderStub[];
extern const char kWebDriverPort[];
+extern const char kWebDriverListenIp[];
+extern const char kMemoryTracker[];
#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
extern const char kImageCacheSizeInBytes[];
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index c21d48b..af93505 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -26,14 +26,19 @@
#include "cobalt/base/c_val.h"
#include "cobalt/base/poller.h"
#include "cobalt/base/tokens.h"
+#include "cobalt/browser/stack_size_constants.h"
#include "cobalt/browser/switches.h"
#include "cobalt/browser/web_module_stat_tracker.h"
+#include "cobalt/css_parser/parser.h"
#include "cobalt/debug/debug_server_module.h"
#include "cobalt/dom/blob.h"
#include "cobalt/dom/csp_delegate_factory.h"
+#include "cobalt/dom/local_storage_database.h"
#include "cobalt/dom/storage.h"
#include "cobalt/dom/url.h"
+#include "cobalt/dom_parser/parser.h"
#include "cobalt/h5vcc/h5vcc.h"
+#include "cobalt/script/javascript_engine.h"
#include "cobalt/storage/storage_manager.h"
namespace cobalt {
@@ -695,7 +700,8 @@
// Start the dedicated thread and create the internal implementation
// object on that thread.
thread_.StartWithOptions(
- base::Thread::Options(MessageLoop::TYPE_DEFAULT, kWebModuleStackSize));
+ base::Thread::Options(MessageLoop::TYPE_DEFAULT,
+ cobalt::browser::kWebModuleStackSize));
DCHECK(message_loop());
message_loop()->PostTask(
diff --git a/src/cobalt/browser/web_module.h b/src/cobalt/browser/web_module.h
index ba8bf30..34f38b2 100644
--- a/src/cobalt/browser/web_module.h
+++ b/src/cobalt/browser/web_module.h
@@ -198,15 +198,6 @@
// can only be called if we have previously suspended the WebModule.
void Resume(render_tree::ResourceProvider* resource_provider);
-#if defined(COBALT_BUILD_TYPE_DEBUG)
- // Non-optimized builds require a bigger stack size.
- static const size_t kBaseStackSize = 2 * 1024 * 1024;
-#else
- static const size_t kBaseStackSize = 256 * 1024;
-#endif
- static const size_t kWebModuleStackSize =
- kBaseStackSize + base::kAsanAdditionalStackSize;
-
private:
// Data required to construct a WebModule, initialized in the constructor and
// passed to |Initialize|.
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 260d24f..1cb6aab 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-14838
\ No newline at end of file
+15306
\ No newline at end of file
diff --git a/src/cobalt/build/gyp_cobalt b/src/cobalt/build/gyp_cobalt
index 030de0f..9ef3934 100755
--- a/src/cobalt/build/gyp_cobalt
+++ b/src/cobalt/build/gyp_cobalt
@@ -208,7 +208,7 @@
'cobalt_fastbuild': os.environ.get('LB_FASTBUILD', 0),
'cobalt_version': gyp_utils.GetBuildNumber(),
'host_os': _GetHostOS(),
- 'CC': os.environ.get('CC', ''),
+ 'CC_HOST': os.environ.get('CC_HOST', os.environ.get('CC','')),
}
if self.platform_config.IsStarboard():
variables['OS'] = 'starboard'
diff --git a/src/cobalt/css_parser/position_parse_structures.cc b/src/cobalt/css_parser/position_parse_structures.cc
index a3f3422..50c13f3 100644
--- a/src/cobalt/css_parser/position_parse_structures.cc
+++ b/src/cobalt/css_parser/position_parse_structures.cc
@@ -119,7 +119,7 @@
break;
}
default:
- NOTREACHED();
+ // Position value couldn't take more than 4 elements.
return false;
}
break;
diff --git a/src/cobalt/dom/dom.gyp b/src/cobalt/dom/dom.gyp
index 4ffeaa6..71ab518 100644
--- a/src/cobalt/dom/dom.gyp
+++ b/src/cobalt/dom/dom.gyp
@@ -258,6 +258,7 @@
'<(DEPTH)/cobalt/storage/storage.gyp:storage',
'<(DEPTH)/cobalt/web_animations/web_animations.gyp:web_animations',
'<(DEPTH)/googleurl/googleurl.gyp:googleurl',
+ '<(DEPTH)/nb/nb.gyp:nb',
],
},
diff --git a/src/cobalt/dom/html_link_element.cc b/src/cobalt/dom/html_link_element.cc
index fd85733..7a4802c 100644
--- a/src/cobalt/dom/html_link_element.cc
+++ b/src/cobalt/dom/html_link_element.cc
@@ -26,6 +26,7 @@
#include "cobalt/dom/document.h"
#include "cobalt/dom/html_element_context.h"
#include "googleurl/src/gurl.h"
+#include "nb/memory_scope.h"
namespace cobalt {
namespace dom {
@@ -54,6 +55,7 @@
// Algorithm for Obtain:
// https://www.w3.org/TR/html5/document-metadata.html#concept-link-obtain
void HTMLLinkElement::Obtain() {
+ TRACK_MEMORY_SCOPE("DOM");
TRACE_EVENT0("cobalt::dom", "HTMLLinkElement::Obtain()");
// Custom, not in any spec.
DCHECK(thread_checker_.CalledOnValidThread());
@@ -107,6 +109,7 @@
}
void HTMLLinkElement::OnLoadingDone(const std::string& content) {
+ TRACK_MEMORY_SCOPE("DOM");
DCHECK(thread_checker_.CalledOnValidThread());
TRACE_EVENT0("cobalt::dom", "HTMLLinkElement::OnLoadingDone()");
diff --git a/src/cobalt/dom/html_media_element.cc b/src/cobalt/dom/html_media_element.cc
index a2851ee..762a0c3 100644
--- a/src/cobalt/dom/html_media_element.cc
+++ b/src/cobalt/dom/html_media_element.cc
@@ -320,9 +320,9 @@
}
}
-WebMediaPlayer::ReadyState HTMLMediaElement::ready_state() const {
+uint16_t HTMLMediaElement::ready_state() const {
MLOG() << ready_state_;
- return ready_state_;
+ return static_cast<uint16_t>(ready_state_);
}
bool HTMLMediaElement::seeking() const {
diff --git a/src/cobalt/dom/html_media_element.h b/src/cobalt/dom/html_media_element.h
index 1d9dfc2..90bfa7c 100644
--- a/src/cobalt/dom/html_media_element.h
+++ b/src/cobalt/dom/html_media_element.h
@@ -95,7 +95,7 @@
kHaveEnoughData = WebMediaPlayer::kReadyStateHaveEnoughData,
};
- WebMediaPlayer::ReadyState ready_state() const;
+ uint16_t ready_state() const;
bool seeking() const;
// Playback state
diff --git a/src/cobalt/dom/html_script_element.cc b/src/cobalt/dom/html_script_element.cc
index e4b5178..5584557 100644
--- a/src/cobalt/dom/html_script_element.cc
+++ b/src/cobalt/dom/html_script_element.cc
@@ -32,6 +32,7 @@
#include "cobalt/script/global_environment.h"
#include "cobalt/script/script_runner.h"
#include "googleurl/src/gurl.h"
+#include "nb/memory_scope.h"
namespace cobalt {
namespace dom {
@@ -111,6 +112,7 @@
// Algorithm for Prepare:
// https://www.w3.org/TR/html5/scripting-1.html#prepare-a-script
void HTMLScriptElement::Prepare() {
+ TRACK_MEMORY_SCOPE("DOM");
// Custom, not in any spec.
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(MessageLoop::current());
@@ -495,6 +497,7 @@
void HTMLScriptElement::Execute(const std::string& content,
const base::SourceLocation& script_location,
bool is_external) {
+ TRACK_MEMORY_SCOPE("DOM");
// If should_execute_ is set to false for whatever reason, then we
// immediately exit.
// When inserted using the document.write() method, script elements execute
diff --git a/src/cobalt/dom/local_storage_database.cc b/src/cobalt/dom/local_storage_database.cc
index e5f2d11..6b7a4cc 100644
--- a/src/cobalt/dom/local_storage_database.cc
+++ b/src/cobalt/dom/local_storage_database.cc
@@ -19,6 +19,7 @@
#include "base/debug/trace_event.h"
#include "cobalt/dom/storage_area.h"
#include "cobalt/storage/storage_manager.h"
+#include "nb/memory_scope.h"
#include "sql/statement.h"
namespace cobalt {
@@ -30,6 +31,7 @@
const int kLatestLocalStorageSchemaVersion = 1;
void SqlInit(storage::SqlContext* sql_context) {
+ TRACK_MEMORY_SCOPE("Storage");
TRACE_EVENT0("cobalt::storage", "LocalStorage::SqlInit()");
sql::Connection* conn = sql_context->sql_connection();
int schema_version;
@@ -74,6 +76,7 @@
void SqlReadValues(const std::string& id,
const LocalStorageDatabase::ReadCompletionCallback& callback,
storage::SqlContext* sql_context) {
+ TRACK_MEMORY_SCOPE("Storage");
scoped_ptr<StorageArea::StorageMap> values(new StorageArea::StorageMap);
sql::Connection* conn = sql_context->sql_connection();
sql::Statement get_values(conn->GetCachedStatement(
@@ -91,6 +94,7 @@
void SqlWrite(const std::string& id, const std::string& key,
const std::string& value, storage::SqlContext* sql_context) {
+ TRACK_MEMORY_SCOPE("Storage");
sql::Connection* conn = sql_context->sql_connection();
sql::Statement write_statement(conn->GetCachedStatement(
SQL_FROM_HERE,
@@ -105,6 +109,7 @@
void SqlDelete(const std::string& id, const std::string& key,
storage::SqlContext* sql_context) {
+ TRACK_MEMORY_SCOPE("Storage");
sql::Connection* conn = sql_context->sql_connection();
sql::Statement delete_statement(
conn->GetCachedStatement(SQL_FROM_HERE,
@@ -117,6 +122,7 @@
}
void SqlClear(const std::string& id, storage::SqlContext* sql_context) {
+ TRACK_MEMORY_SCOPE("Storage");
sql::Connection* conn = sql_context->sql_connection();
sql::Statement clear_statement(
conn->GetCachedStatement(SQL_FROM_HERE,
@@ -142,12 +148,14 @@
void LocalStorageDatabase::ReadAll(const std::string& id,
const ReadCompletionCallback& callback) {
+ TRACK_MEMORY_SCOPE("Storage");
Init();
storage_->GetSqlContext(base::Bind(&SqlReadValues, id, callback));
}
void LocalStorageDatabase::Write(const std::string& id, const std::string& key,
const std::string& value) {
+ TRACK_MEMORY_SCOPE("Storage");
Init();
storage_->GetSqlContext(base::Bind(&SqlWrite, id, key, value));
storage_->Flush();
diff --git a/src/cobalt/dom/typed_array.h b/src/cobalt/dom/typed_array.h
index 266e002..e901a70 100644
--- a/src/cobalt/dom/typed_array.h
+++ b/src/cobalt/dom/typed_array.h
@@ -17,14 +17,16 @@
#ifndef COBALT_DOM_TYPED_ARRAY_H_
#define COBALT_DOM_TYPED_ARRAY_H_
-#include <memory.h> // for memcpy
-
#include "base/logging.h"
#include "base/stringprintf.h"
#include "cobalt/dom/array_buffer_view.h"
#include "cobalt/script/environment_settings.h"
#include "cobalt/script/exception_state.h"
+#if defined(STARBOARD)
+#include "starboard/memory.h"
+#endif
+
namespace cobalt {
namespace dom {
@@ -50,7 +52,11 @@
uint32 length)
: ArrayBufferView(new ArrayBuffer(settings, length * kBytesPerElement)) {
DCHECK_EQ(this->length(), length);
+#if defined(STARBOARD)
+ SbMemoryCopy(this->data(), data, length * kBytesPerElement);
+#else
memcpy(this->data(), data, length * kBytesPerElement);
+#endif
}
// Creates a new TypedArray and copies the elements of 'other' into this.
@@ -123,8 +129,13 @@
}
uint32 source_offset = 0;
while (source_offset < source->length()) {
+#if defined(STARBOARD)
+ SbMemoryCopy(data() + offset, source->data() + source_offset,
+ sizeof(ElementType));
+#else
memcpy(data() + offset, source->data() + source_offset,
sizeof(ElementType));
+#endif
++offset;
++source_offset;
}
@@ -138,14 +149,22 @@
// Write a single element of the array.
void Set(uint32 index, ElementType val) {
if (index < length()) {
+#if defined(STARBOARD)
+ SbMemoryCopy(data() + index, &val, sizeof(ElementType));
+#else
memcpy(data() + index, &val, sizeof(ElementType));
+#endif
}
}
ElementType Get(uint32 index) const {
if (index < length()) {
ElementType val;
+#if defined(STARBOARD)
+ SbMemoryCopy(&val, data() + index, sizeof(ElementType));
+#else
memcpy(&val, data() + index, sizeof(ElementType));
+#endif
return val;
} else {
// TODO: an out of bounds index should return undefined.
diff --git a/src/cobalt/layout_tests/layout_tests.cc b/src/cobalt/layout_tests/layout_tests.cc
index c2e63c0..18dccb1 100644
--- a/src/cobalt/layout_tests/layout_tests.cc
+++ b/src/cobalt/layout_tests/layout_tests.cc
@@ -92,7 +92,7 @@
scoped_refptr<render_tree::animations::AnimateNode> animate_node =
new render_tree::animations::AnimateNode(layout_results.render_tree);
scoped_refptr<render_tree::Node> animated_tree =
- animate_node->Apply(layout_results.layout_time);
+ animate_node->Apply(layout_results.layout_time).animated;
bool results =
pixel_tester.TestTree(animated_tree, GetParam().base_file_path);
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-gradient-in-middle-expected.png b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-gradient-in-middle-expected.png
new file mode 100644
index 0000000..39205b9
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-gradient-in-middle-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-gradient-in-middle.html b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-gradient-in-middle.html
new file mode 100644
index 0000000..4972e3d
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-gradient-in-middle.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <style>
+ div {
+ background: linear-gradient(to left, yellow 20%, purple 70%);
+ width: 100px;
+ height: 100px;
+ }
+ </style>
+</head>
+<body>
+ <div></div>
+</body>
+</html>
+
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-multistop-one-dimensional-expected.png b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-multistop-one-dimensional-expected.png
new file mode 100644
index 0000000..c2bdcce
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-multistop-one-dimensional-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-multistop-one-dimensional-gradient-expected.png b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-multistop-one-dimensional-gradient-expected.png
new file mode 100644
index 0000000..c2bdcce
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-multistop-one-dimensional-gradient-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-multistop-one-dimensional-gradient.html b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-multistop-one-dimensional-gradient.html
new file mode 100644
index 0000000..03019b7
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-multistop-one-dimensional-gradient.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <style>
+ div {
+ background: linear-gradient(to left, yellow, purple, green);
+ width: 100px;
+ height: 100px;
+ }
+ </style>
+</head>
+<body>
+ <div></div>
+</body>
+</html>
+
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-bottom-linear-gradient-expected.png b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-bottom-linear-gradient-expected.png
new file mode 100644
index 0000000..ac7cae8
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-bottom-linear-gradient-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-bottom-linear-gradient.html b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-bottom-linear-gradient.html
new file mode 100644
index 0000000..36cdbcb
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-bottom-linear-gradient.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <style>
+ div {
+ background: linear-gradient(to bottom, yellow, transparent);
+ width: 100px;
+ height: 100px;
+ }
+ </style>
+</head>
+<body>
+ <div></div>
+</body>
+</html>
+
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-left-linear-gradient-expected.png b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-left-linear-gradient-expected.png
new file mode 100644
index 0000000..996008f
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-left-linear-gradient-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-left-linear-gradient.html b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-left-linear-gradient.html
new file mode 100644
index 0000000..b161dbd
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-left-linear-gradient.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <style>
+ div {
+ background: linear-gradient(to left, yellow, transparent);
+ width: 100px;
+ height: 100px;
+ }
+ </style>
+</head>
+<body>
+ <div></div>
+</body>
+</html>
+
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-right-linear-gradient-expected.png b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-right-linear-gradient-expected.png
new file mode 100644
index 0000000..c96bfe7
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-right-linear-gradient-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-right-linear-gradient.html b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-right-linear-gradient.html
new file mode 100644
index 0000000..e56d490
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-right-linear-gradient.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <style>
+ div {
+ background: linear-gradient(to right, yellow, transparent);
+ width: 100px;
+ height: 100px;
+ }
+ </style>
+</head>
+<body>
+ <div></div>
+</body>
+</html>
+
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-top-linear-gradient-expected.png b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-top-linear-gradient-expected.png
new file mode 100644
index 0000000..e4ec68c
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-top-linear-gradient-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-top-linear-gradient.html b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-top-linear-gradient.html
new file mode 100644
index 0000000..683c1da
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-images/4-1-2-to-top-linear-gradient.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <style>
+ div {
+ background: linear-gradient(to top, yellow, transparent);
+ width: 100px;
+ height: 100px;
+ }
+ </style>
+</head>
+<body>
+ <div></div>
+</body>
+</html>
+
diff --git a/src/cobalt/layout_tests/testdata/css3-images/layout_tests.txt b/src/cobalt/layout_tests/testdata/css3-images/layout_tests.txt
index d2988a2..6abfa12 100644
--- a/src/cobalt/layout_tests/testdata/css3-images/layout_tests.txt
+++ b/src/cobalt/layout_tests/testdata/css3-images/layout_tests.txt
@@ -1,9 +1,15 @@
4-1-2-corner-to-corner-linear-gradient
+4-1-2-gradient-in-middle
4-1-2-linear-gradient-with-same-stop-positions
4-1-2-linear-gradient-with-transparency
+4-1-2-multistop-one-dimensional-gradient
4-1-2-simple-yellow-to-blue-arbitrary-angle-linear-gradient
4-1-2-simple-yellow-to-blue-vertical-linear-gradient
4-1-2-three-color-linear-gradient
+4-1-2-to-bottom-linear-gradient
+4-1-2-to-left-linear-gradient
+4-1-2-to-right-linear-gradient
+4-1-2-to-top-linear-gradient
4-2-4-closest-side-radial-gradient
4-2-4-radial-gradient-non-centered
4-2-4-radial-gradient-with-transparency
diff --git a/src/cobalt/loader/file_fetcher.cc b/src/cobalt/loader/file_fetcher.cc
index 1cbe61f..83ee7a6 100644
--- a/src/cobalt/loader/file_fetcher.cc
+++ b/src/cobalt/loader/file_fetcher.cc
@@ -40,7 +40,7 @@
// Ensure the request does not attempt to navigate outside the whitelisted
// directory.
- if (file_path_.ReferencesParent()) {
+ if (file_path_.ReferencesParent() || file_path_.IsAbsolute()) {
handler->OnError(this, PlatformFileErrorToString(
base::PLATFORM_FILE_ERROR_ACCESS_DENIED));
return;
diff --git a/src/cobalt/loader/resource_cache.h b/src/cobalt/loader/resource_cache.h
index 2f4ce23..1c3e4e6 100644
--- a/src/cobalt/loader/resource_cache.h
+++ b/src/cobalt/loader/resource_cache.h
@@ -47,6 +47,13 @@
template <typename CacheType>
class ResourceCache;
+enum CallbackType {
+ kOnLoadingSuccessCallbackType,
+ kOnLoadingFailureCallbackType,
+ kOnLoadingErrorCallbackType,
+ kCallbackTypeCount,
+};
+
//////////////////////////////////////////////////////////////////////////
// CachedResource - Declarations
//////////////////////////////////////////////////////////////////////////
@@ -121,17 +128,11 @@
private:
friend class base::RefCountedThreadSafe<CachedResource>;
friend class OnLoadedCallbackHandler;
+ friend class ResourceCache<CacheType>;
typedef std::list<base::Closure> CallbackList;
typedef std::list<base::Closure>::iterator CallbackListIterator;
- enum CallbackType {
- kOnLoadingSuccessCallbackType,
- kOnLoadingFailureCallbackType,
- kOnLoadingErrorCallbackType,
- kCallbackTypeCount,
- };
-
~CachedResource();
// Callbacks for decoders.
@@ -274,10 +275,8 @@
resource_ = resource;
loader_.reset();
- resource_cache_->NotifyResourceSuccessfullyLoaded(this);
- // To avoid the last reference of this object get deleted in the callbacks.
- scoped_refptr<CachedResource<CacheType> > holder(this);
- RunCallbacks(kOnLoadingSuccessCallbackType);
+ resource_cache_->NotifyResourceLoadingComplete(this,
+ kOnLoadingSuccessCallbackType);
}
template <typename CacheType>
@@ -287,9 +286,8 @@
LOG(WARNING) << "Warning while loading '" << url_ << "': " << message;
loader_.reset();
- // To avoid the last reference of this object get deleted in the callbacks.
- scoped_refptr<CachedResource<CacheType> > holder(this);
- RunCallbacks(kOnLoadingFailureCallbackType);
+ resource_cache_->NotifyResourceLoadingComplete(this,
+ kOnLoadingFailureCallbackType);
}
template <typename CacheType>
@@ -299,9 +297,8 @@
LOG(ERROR) << "Error while loading '" << url_ << "': " << error;
loader_.reset();
- // To avoid the last reference of this object get deleted in the callbacks.
- scoped_refptr<CachedResource<CacheType> > holder(this);
- RunCallbacks(kOnLoadingErrorCallbackType);
+ resource_cache_->NotifyResourceLoadingComplete(this,
+ kOnLoadingErrorCallbackType);
}
template <typename CacheType>
@@ -328,7 +325,7 @@
void CachedResource<CacheType>::RunCallbacks(CallbackType type) {
DCHECK(cached_resource_thread_checker_.CalledOnValidThread());
- // To avoid the list gets altered in the callbacks.
+ // To avoid the list getting altered in the callbacks.
CallbackList callback_list = callback_lists[type];
CallbackListIterator callback_iter;
for (callback_iter = callback_list.begin();
@@ -391,6 +388,15 @@
typedef
typename CachedResourceType::CreateLoaderFunction CreateLoaderFunction;
+ struct ResourceCallbackInfo {
+ ResourceCallbackInfo(CachedResourceType* cached_resource,
+ CallbackType callback_type)
+ : cached_resource(cached_resource), callback_type(callback_type) {}
+
+ CachedResourceType* cached_resource;
+ CallbackType callback_type;
+ };
+
ResourceCache(const std::string& name, uint32 cache_capacity,
const CreateLoaderFunction& create_loader_function);
@@ -421,12 +427,17 @@
typedef base::hash_map<std::string, CachedResourceType*> CachedResourceMap;
typedef typename CachedResourceMap::iterator CachedResourceMapIterator;
+ typedef base::hash_set<std::string> ResourceSet;
+ typedef base::linked_hash_map<std::string, ResourceCallbackInfo>
+ ResourceCallbackMap;
+
typedef base::linked_hash_map<std::string, scoped_refptr<ResourceType> >
ResourceMap;
typedef typename ResourceMap::iterator ResourceMapIterator;
- // Called by CachedResource objects after they are successfully loaded.
- void NotifyResourceSuccessfullyLoaded(CachedResourceType* cached_resource);
+ // Called by CachedResource objects after they finish loading.
+ void NotifyResourceLoadingComplete(CachedResourceType* cached_resource,
+ CallbackType callback_type);
// Called by the destructor of CachedResource to remove CachedResource from
// |cached_resource_map_| and either immediately free the resource from memory
@@ -434,10 +445,24 @@
// cache is over its memory limit.
void NotifyResourceDestroyed(CachedResourceType* cached_resource);
+ // Reclaims memory from unreferenced cache objects until total cache memory
+ // is reduced to |bytes_to_reclaim_down_to|. In the case where the desired
+ // memory cannot be freed, pending callbacks are processed (potentially
+ // enabling additional resources to be reclaimed), and memory reclamation is
+ // attempted again.
+ void ReclaimMemoryAndMaybeProcessPendingCallbacks(
+ uint32 bytes_to_reclaim_down_to);
// Releases unreferenced cache objects until our total cache memory usage is
// less than or equal to |bytes_to_reclaim_down_to|, or until there are no
// more unreferenced cache objects to release.
- void ReclaimMemory(uint32 bytes_to_reclaim_down_to);
+ void ReclaimMemory(uint32 bytes_to_reclaim_down_to, bool log_warning_if_over);
+
+ // Calls ProcessPendingCallbacks() if
+ // |callback_blocking_loading_resource_set_| is empty.
+ void ProcessPendingCallbacksIfUnblocked();
+ // Processes all pending callbacks regardless of the state of
+ // |callback_blocking_loading_resource_set_|.
+ void ProcessPendingCallbacks();
// The name of this resource cache object, useful while debugging.
const std::string name_;
@@ -448,6 +473,28 @@
csp::SecurityCallback security_callback_;
+ // The resource cache attempts to batch callbacks as much as possible to try
+ // to ensure that events triggered by the callbacks occur together. It
+ // accomplishes this by waiting for all active loads to complete before
+ // processing any of their callbacks. However, to ensure that callbacks are
+ // processed in a timely manner as well, active loads are placed into two
+ // buckets: callback blocking and non-callback blocking. While no callbacks
+ // are pending, all active loads are added as callback blocking. As soon as
+ // a callback is pending, any additional load requests are added as
+ // non-callback blocking. As soon as all of the callback blocking loads are
+ // finished, the pending callbacks are processed, the non-callback blocking
+ // loads become callback blocking loads, and the process repeats itself.
+
+ // Currently loading resources that block any pending callbacks from running.
+ ResourceSet callback_blocking_loading_resource_set_;
+ // Currently loading resources that do not block the pending callbacks from
+ // running. After pending callbacks run, these become blocking.
+ ResourceSet non_callback_blocking_loading_resource_set_;
+ // Resources that have completed loading and have callbacks pending.
+ ResourceCallbackMap pending_callback_map_;
+ // Whether or not ProcessPendingCallbacks() is running.
+ bool is_processing_pending_callbacks_;
+
// |cached_resource_map_| stores the cached resources that are currently
// referenced.
CachedResourceMap cached_resource_map_;
@@ -476,6 +523,7 @@
: name_(name),
cache_capacity_(cache_capacity),
create_loader_function_(create_loader_function),
+ is_processing_pending_callbacks_(false),
size_in_bytes_(base::StringPrintf("%s.Size", name_.c_str()), 0,
"Total number of bytes currently used by the cache."),
capacity_in_bytes_(base::StringPrintf("%s.Capacity", name_.c_str()),
@@ -512,8 +560,21 @@
return cached_resource;
}
- // If the resource doesn't exist, create a cached resource and fetch the
- // resource based on the url.
+ // If we reach this point, then the resource doesn't exist yet.
+
+ // Add the resource to a loading set. If no current resources have pending
+ // callbacks, then this resource will block callbacks until it is decoded.
+ // However, if there are resources with pending callbacks, then the decoding
+ // of this resource won't block the callbacks from occurring. This ensures
+ // that a steady stream of new resources won't prevent callbacks from ever
+ // occurring.
+ if (pending_callback_map_.empty()) {
+ callback_blocking_loading_resource_set_.insert(url.spec());
+ } else {
+ non_callback_blocking_loading_resource_set_.insert(url.spec());
+ }
+
+ // Create the cached resource and fetch its resource based on the url.
scoped_refptr<CachedResourceType> cached_resource(new CachedResourceType(
url, security_callback_, create_loader_function_, this));
cached_resource_map_.insert(
@@ -526,55 +587,106 @@
DCHECK(resource_cache_thread_checker_.CalledOnValidThread());
cache_capacity_ = capacity;
capacity_in_bytes_ = capacity;
- ReclaimMemory(cache_capacity_);
+ ReclaimMemoryAndMaybeProcessPendingCallbacks(cache_capacity_);
}
template <typename CacheType>
void ResourceCache<CacheType>::Purge() {
DCHECK(resource_cache_thread_checker_.CalledOnValidThread());
- ReclaimMemory(0);
+ ReclaimMemoryAndMaybeProcessPendingCallbacks(0);
}
template <typename CacheType>
-void ResourceCache<CacheType>::NotifyResourceSuccessfullyLoaded(
- CachedResourceType* cached_resource) {
+void ResourceCache<CacheType>::NotifyResourceLoadingComplete(
+ CachedResourceType* cached_resource, CallbackType callback_type) {
DCHECK(resource_cache_thread_checker_.CalledOnValidThread());
+ const std::string& url = cached_resource->url().spec();
if (cached_resource->TryGetResource()) {
size_in_bytes_ +=
CacheType::GetEstimatedSizeInBytes(cached_resource->TryGetResource());
- if (size_in_bytes_ > cache_capacity_) {
- ReclaimMemory(cache_capacity_);
- }
}
+
+ // Remove the resource from its loading set. It should exist in exactly one
+ // of the loading sets.
+ if (callback_blocking_loading_resource_set_.erase(url)) {
+ DCHECK(non_callback_blocking_loading_resource_set_.find(url) ==
+ non_callback_blocking_loading_resource_set_.end());
+ } else if (!non_callback_blocking_loading_resource_set_.erase(url)) {
+ DCHECK(false);
+ }
+
+ // Add a callback for the resource that just finished loading to the pending
+ // callbacks.
+ pending_callback_map_.insert(std::make_pair(
+ url, ResourceCallbackInfo(cached_resource, callback_type)));
+
+ ProcessPendingCallbacksIfUnblocked();
+ ReclaimMemoryAndMaybeProcessPendingCallbacks(cache_capacity_);
}
template <typename CacheType>
void ResourceCache<CacheType>::NotifyResourceDestroyed(
CachedResourceType* cached_resource) {
DCHECK(resource_cache_thread_checker_.CalledOnValidThread());
+ const std::string& url = cached_resource->url().spec();
- std::string url = cached_resource->url().spec();
cached_resource_map_.erase(url);
DCHECK(unreference_cached_resource_map_.find(url) ==
unreference_cached_resource_map_.end());
+
// Check to see if this was a loaded resource.
if (cached_resource->TryGetResource()) {
// Add it into the unreferenced cached resource map, so that it will be
// retained while memory is available for it in the cache.
unreference_cached_resource_map_.insert(
std::make_pair(url, cached_resource->TryGetResource()));
- // Try to reclaim some memory.
- ReclaimMemory(cache_capacity_);
+ }
+
+ // Remove the resource from any loading or pending container that it is in.
+ // It should never exist in more than one of the containers.
+ if (callback_blocking_loading_resource_set_.erase(url)) {
+ DCHECK(non_callback_blocking_loading_resource_set_.find(url) ==
+ non_callback_blocking_loading_resource_set_.end());
+ DCHECK(pending_callback_map_.find(url) == pending_callback_map_.end());
+ } else if (non_callback_blocking_loading_resource_set_.erase(url)) {
+ DCHECK(pending_callback_map_.find(url) == pending_callback_map_.end());
+ } else {
+ pending_callback_map_.erase(url);
+ }
+
+ // Only process pending callbacks and attempt to reclaim memory if
+ // NotifyResourceDestroyed() wasn't called from within
+ // ProcessPendingCallbacks(). This prevents recursion and redundant
+ // processing.
+ if (!is_processing_pending_callbacks_) {
+ ProcessPendingCallbacksIfUnblocked();
+ ReclaimMemory(cache_capacity_, true /*log_warning_if_over*/);
}
}
template <typename CacheType>
-void ResourceCache<CacheType>::ReclaimMemory(uint32 bytes_to_reclaim_down_to) {
+void ResourceCache<CacheType>::ReclaimMemoryAndMaybeProcessPendingCallbacks(
+ uint32 bytes_to_reclaim_down_to) {
+ ReclaimMemory(bytes_to_reclaim_down_to, false /*log_warning_if_over*/);
+ // If the current size of the cache is still greater than
+ // |bytes_to_reclaim_down_to| after reclaiming memory, then process any
+ // pending callbacks and try again. References to the cached resources are
+ // potentially being held until the callbacks run, so processing them may
+ // enable more memory to be reclaimed.
+ if (size_in_bytes_ > bytes_to_reclaim_down_to) {
+ ProcessPendingCallbacks();
+ ReclaimMemory(bytes_to_reclaim_down_to, true /*log_warning_if_over*/);
+ }
+}
+
+template <typename CacheType>
+void ResourceCache<CacheType>::ReclaimMemory(uint32 bytes_to_reclaim_down_to,
+ bool log_warning_if_over) {
DCHECK(resource_cache_thread_checker_.CalledOnValidThread());
- while (size_in_bytes_.value() > bytes_to_reclaim_down_to &&
+ while (size_in_bytes_ > bytes_to_reclaim_down_to &&
!unreference_cached_resource_map_.empty()) {
// The first element is the earliest-inserted element.
scoped_refptr<ResourceType> resource =
@@ -588,14 +700,47 @@
size_in_bytes_ -= first_resource_size;
}
- // Make sure that |size_in_bytes_| is less than or equal to |cache_capacity_|,
- // otherwise it means that |unreference_cached_resource_map_| is empty. We
- // have to increase the size of |cache_capacity_| if the system memory is
- // large enough or evict resources from the cache even though they are still
- // in use.
- DLOG_IF(WARNING, size_in_bytes_.value() > cache_capacity_)
- << "cached size: " << size_in_bytes_
- << ", cache capacity: " << cache_capacity_;
+ if (log_warning_if_over) {
+ // Log a warning if we're still over |bytes_to_reclaim_down_to| after
+ // attempting to reclaim memory. This can occur validly when the size of
+ // the referenced images exceeds the target size.
+ DLOG_IF(WARNING, size_in_bytes_ > bytes_to_reclaim_down_to)
+ << "cached size: " << size_in_bytes_
+ << ", target size: " << bytes_to_reclaim_down_to;
+ }
+}
+
+template <typename CacheType>
+void ResourceCache<CacheType>::ProcessPendingCallbacksIfUnblocked() {
+ if (callback_blocking_loading_resource_set_.empty()) {
+ ProcessPendingCallbacks();
+
+ // Now that we've processed the callbacks, if there are any non-blocking
+ // loading resources, then they're becoming blocking. Simply swap the two
+ // sets, rather than copying the contents over.
+ if (!non_callback_blocking_loading_resource_set_.empty()) {
+ callback_blocking_loading_resource_set_.swap(
+ non_callback_blocking_loading_resource_set_);
+ }
+ }
+}
+
+template <typename CacheType>
+void ResourceCache<CacheType>::ProcessPendingCallbacks() {
+ DCHECK(resource_cache_thread_checker_.CalledOnValidThread());
+
+ is_processing_pending_callbacks_ = true;
+ while (!pending_callback_map_.empty()) {
+ ResourceCallbackInfo& callback_info = pending_callback_map_.front().second;
+
+ // To avoid the last reference of this object getting deleted in the
+ // callbacks.
+ scoped_refptr<CachedResourceType> holder(callback_info.cached_resource);
+ callback_info.cached_resource->RunCallbacks(callback_info.callback_type);
+
+ pending_callback_map_.erase(pending_callback_map_.begin());
+ }
+ is_processing_pending_callbacks_ = false;
}
} // namespace loader
diff --git a/src/cobalt/render_tree/animations/animate_node.cc b/src/cobalt/render_tree/animations/animate_node.cc
index 6363ec9..0663359 100644
--- a/src/cobalt/render_tree/animations/animate_node.cc
+++ b/src/cobalt/render_tree/animations/animate_node.cc
@@ -21,6 +21,7 @@
#include "base/debug/trace_event.h"
#include "cobalt/base/enable_if.h"
#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/math/transform_2d.h"
#include "cobalt/render_tree/child_iterator.h"
#include "cobalt/render_tree/node_visitor.h"
@@ -214,6 +215,158 @@
animated_ = true;
}
+// A helper class for computing the tightest bounding rectangle that encloses
+// all animated subtrees. It is meant to be applied to an already-animated
+// render tree, using a TraverseList provided by
+// ApplyVisitor::animated_traverse_list(). The result of the visit can be
+// retrieved from bounds(). This calculation is useful for determining a
+// "dirty rectangle" to minimize drawing.
+class AnimateNode::BoundsVisitor : public NodeVisitor {
+ public:
+ BoundsVisitor(const TraverseList& traverse_list, base::TimeDelta time_offset,
+ base::TimeDelta since);
+
+ void Visit(animations::AnimateNode* /* animate */) OVERRIDE {
+ // An invariant of AnimateNodes is that they should never contain descendant
+ // AnimateNodes.
+ NOTREACHED();
+ }
+ // Immediately switch to a templated visitor function.
+ void Visit(CompositionNode* composition) OVERRIDE { VisitNode(composition); }
+ void Visit(FilterNode* text) OVERRIDE { VisitNode(text); }
+ void Visit(ImageNode* image) OVERRIDE { VisitNode(image); }
+ void Visit(MatrixTransformNode* transform) OVERRIDE { VisitNode(transform); }
+ void Visit(PunchThroughVideoNode* punch_through) OVERRIDE {
+ VisitNode(punch_through);
+ }
+ void Visit(RectNode* rect) OVERRIDE { VisitNode(rect); }
+ void Visit(RectShadowNode* rect) OVERRIDE { VisitNode(rect); }
+ void Visit(TextNode* text) OVERRIDE { VisitNode(text); }
+
+ const math::RectF& bounds() const { return bounds_; }
+
+ private:
+ template <typename T>
+ typename base::enable_if<!ChildIterator<T>::has_children>::type VisitNode(
+ T* node);
+ template <typename T>
+ typename base::enable_if<ChildIterator<T>::has_children>::type VisitNode(
+ T* node);
+ void ProcessAnimatedNodeBounds(const TraverseListEntry& entry,
+ render_tree::Node* node);
+ TraverseListEntry AdvanceIterator(Node* node);
+
+ void ApplyTransform(Node* node);
+ void ApplyTransform(CompositionNode* node);
+ void ApplyTransform(MatrixTransformNode* node);
+
+ // The time offset to be passed in to individual animations.
+ base::TimeDelta time_offset_;
+
+ // The time when we "start" checking for active animations. In other words,
+ // if an animation had expired *before* |since_|, then it is not considered
+ // animated, and not considered for the bounding box.
+ base::TimeDelta since_;
+
+ // A list of nodes that we are allowed to traverse into (i.e. a traversal that
+ // guides us to animated nodes). It assumes that a pre-order traversal will
+ // be taken.
+ const TraverseList& traverse_list_;
+
+ // An iterator pointing to the next valid render tree node to visit.
+ TraverseList::const_iterator iterator_;
+
+ // The resulting bounding box surrounding all active animations.
+ math::RectF bounds_;
+
+ // We need to maintain a "current" transform as we traverse the tree, so that
+ // we know the transformed bounding boxes of nodes when we reach them.
+ math::Matrix3F transform_;
+};
+
+AnimateNode::BoundsVisitor::BoundsVisitor(const TraverseList& traverse_list,
+ base::TimeDelta time_offset,
+ base::TimeDelta since)
+ : time_offset_(time_offset),
+ since_(since),
+ traverse_list_(traverse_list),
+ transform_(math::Matrix3F::Identity()) {
+ iterator_ = traverse_list_.begin();
+}
+
+template <typename T>
+typename base::enable_if<!ChildIterator<T>::has_children>::type
+AnimateNode::BoundsVisitor::VisitNode(T* node) {
+ TraverseListEntry current_entry = AdvanceIterator(node);
+
+ DCHECK(current_entry.animations);
+ ProcessAnimatedNodeBounds(current_entry, node);
+}
+
+template <typename T>
+typename base::enable_if<ChildIterator<T>::has_children>::type
+AnimateNode::BoundsVisitor::VisitNode(T* node) {
+ TraverseListEntry current_entry = AdvanceIterator(node);
+
+ math::Matrix3F old_transform = transform_;
+ ApplyTransform(node);
+
+ // Traverse the child nodes, but only the ones that are on the
+ // |traverse_list_|. In particular, the next node we are allowed to visit
+ // is the one in the traverse list pointed to by |iterator_->node|.
+ ChildIterator<T> child_iterator(node);
+ while (Node* child = child_iterator.GetCurrent()) {
+ if (iterator_ == traverse_list_.end()) {
+ // If we've reached the end of |traverse_list_| then we are done
+ // iterating and it's time to return.
+ break;
+ }
+
+ if (child == iterator_->node) {
+ // If one of our children is next up on the path to animation, traverse
+ // into it.
+ child->Accept(this);
+ }
+
+ child_iterator.Next();
+ }
+ transform_ = old_transform;
+
+ if (current_entry.animations) {
+ ProcessAnimatedNodeBounds(current_entry, node);
+ }
+}
+
+void AnimateNode::BoundsVisitor::ProcessAnimatedNodeBounds(
+ const TraverseListEntry& entry, render_tree::Node* node) {
+ TRACE_EVENT0("cobalt::renderer",
+ "AnimateNode::BoundsVisitor::ProcessAnimatedNodeBounds()");
+ if (entry.animations->GetExpiry() >= since_) {
+ bounds_.Union(transform_.MapRect(node->GetBounds()));
+ }
+}
+
+AnimateNode::TraverseListEntry AnimateNode::BoundsVisitor::AdvanceIterator(
+ Node* node) {
+ // Check that the iterator that we are advancing past is indeed the one we
+ // expect it to be.
+ DCHECK_EQ(node, iterator_->node);
+ return *(iterator_++);
+}
+
+void AnimateNode::BoundsVisitor::ApplyTransform(Node* node) {
+ UNREFERENCED_PARAMETER(node);
+}
+
+void AnimateNode::BoundsVisitor::ApplyTransform(CompositionNode* node) {
+ transform_ = transform_ * math::TranslateMatrix(node->data().offset().x(),
+ node->data().offset().y());
+}
+
+void AnimateNode::BoundsVisitor::ApplyTransform(MatrixTransformNode* node) {
+ transform_ = transform_ * node->data().transform;
+}
+
// A helper render tree visitor class used to apply compiled sub render-tree
// animations. Only one of these visitors is needed to visit an entire render
// tree.
@@ -242,6 +395,11 @@
// final animated result can be pulled from this visitor.
const scoped_refptr<Node>& animated() const { return animated_; }
+ // As we compute the animated nodes, we create a new traverse list that leads
+ // to the newly created animated nodes. This can be used afterwards to
+ // calculate the bounding boxes around the active animated nodes.
+ TraverseList* animated_traverse_list() { return &animated_traverse_list_; }
+
private:
template <typename T>
typename base::enable_if<!ChildIterator<T>::has_children>::type VisitNode(
@@ -250,8 +408,8 @@
typename base::enable_if<ChildIterator<T>::has_children>::type VisitNode(
T* node);
template <typename T>
- void ApplyAnimations(const TraverseListEntry& entry,
- typename T::Builder* builder);
+ scoped_refptr<Node> ApplyAnimations(const TraverseListEntry& entry,
+ typename T::Builder* builder);
TraverseListEntry AdvanceIterator(Node* node);
// The time offset to be passed in to individual animations.
@@ -262,6 +420,12 @@
// guides us to animated nodes). It assumes that a pre-order traversal will
// be taken.
const TraverseList& traverse_list_;
+
+ // As we animate the nodes, we also keep track of a new traverse list that
+ // replaces the non-animated nodes for the animated nodes, so that we can
+ // go through and traverse the animated nodes after they have been animated.
+ TraverseList animated_traverse_list_;
+
// An iterator pointing to the next valid render tree node to visit.
TraverseList::const_iterator iterator_;
};
@@ -269,6 +433,7 @@
AnimateNode::ApplyVisitor::ApplyVisitor(const TraverseList& traverse_list,
base::TimeDelta time_offset)
: time_offset_(time_offset), traverse_list_(traverse_list) {
+ animated_traverse_list_.reserve(traverse_list.size());
iterator_ = traverse_list_.begin();
}
@@ -280,8 +445,9 @@
// have animations.
DCHECK(current_entry.animations);
typename T::Builder builder(node->data());
- ApplyAnimations<T>(current_entry, &builder);
- animated_ = new T(builder);
+ animated_ = ApplyAnimations<T>(current_entry, &builder);
+ animated_traverse_list_.push_back(
+ TraverseListEntry(animated_, current_entry.animations));
}
template <typename T>
@@ -289,6 +455,10 @@
AnimateNode::ApplyVisitor::VisitNode(T* node) {
TraverseListEntry current_entry = AdvanceIterator(node);
+ size_t animated_traverse_list_index = animated_traverse_list_.size();
+ animated_traverse_list_.push_back(
+ TraverseListEntry(NULL, current_entry.animations));
+
// Traverse the child nodes, but only the ones that are on the
// |traverse_list_|. In particular, the next node we are allowed to visit
// is the one in the traverse list pointed to by |iterator_->node|.
@@ -326,8 +496,7 @@
// be passed into the animations.
builder.emplace(node->data());
}
- ApplyAnimations<T>(current_entry, &(*builder));
- animated_ = new T(*builder);
+ animated_ = ApplyAnimations<T>(current_entry, &(*builder));
} else {
// If there were no animations targeting this node directly, then its
// children must have been modified since otherwise it wouldn't be in
@@ -335,11 +504,13 @@
DCHECK(children_modified);
animated_ = new T(child_iterator.TakeReplacedChildrenBuilder());
}
+
+ animated_traverse_list_[animated_traverse_list_index].node = animated_;
}
template <typename T>
-void AnimateNode::ApplyVisitor::ApplyAnimations(const TraverseListEntry& entry,
- typename T::Builder* builder) {
+scoped_refptr<Node> AnimateNode::ApplyVisitor::ApplyAnimations(
+ const TraverseListEntry& entry, typename T::Builder* builder) {
TRACE_EVENT0("cobalt::renderer",
"AnimateNode::ApplyVisitor::ApplyAnimations()");
// Cast to the specific type we expect these animations to have.
@@ -353,6 +524,8 @@
iter != typed_node_animations->data().animations.end(); ++iter) {
iter->Run(builder, time_offset_);
}
+
+ return new T(*builder);
}
AnimateNode::TraverseListEntry AnimateNode::ApplyVisitor::AdvanceIterator(
@@ -374,15 +547,66 @@
CommonInit(Builder::InternalMap(), source);
}
-scoped_refptr<Node> AnimateNode::Apply(base::TimeDelta time_offset) {
+// Helper class to refcount wrap a TraverseList object so that it can be
+// passed around in a callback.
+class AnimateNode::RefCountedTraversalList
+ : public base::RefCounted<RefCountedTraversalList> {
+ public:
+ explicit RefCountedTraversalList(TraverseList* to_swap_in) {
+ traverse_list_.swap(*to_swap_in);
+ }
+ const TraverseList& traverse_list() const { return traverse_list_; }
+
+ private:
+ friend class base::RefCounted<RefCountedTraversalList>;
+ ~RefCountedTraversalList() {}
+
+ TraverseList traverse_list_;
+};
+
+// static
+math::RectF AnimateNode::GetAnimationBoundsSince(
+ const scoped_refptr<RefCountedTraversalList>& traverse_list,
+ base::TimeDelta time_offset, const scoped_refptr<Node>& animated,
+ base::TimeDelta since) {
+ TRACE_EVENT0("cobalt::renderer", "AnimateNode::GetAnimationBoundsSince()");
+
+ BoundsVisitor bounds_visitor(traverse_list->traverse_list(), time_offset,
+ since);
+ animated->Accept(&bounds_visitor);
+ return bounds_visitor.bounds();
+}
+
+namespace {
+// Helper function to always return an empty bounding rectangle.
+math::RectF ReturnTrivialEmptyRectBound(base::TimeDelta since) {
+ UNREFERENCED_PARAMETER(since);
+ return math::RectF();
+}
+} // namespace
+
+AnimateNode::AnimateResults AnimateNode::Apply(base::TimeDelta time_offset) {
TRACE_EVENT0("cobalt::renderer", "AnimateNode::Apply()");
+ AnimateResults results;
if (traverse_list_.empty()) {
- return source_;
+ results.animated = source_;
+ // There are no animations, so there is no bounding rectangle, so setup the
+ // bounding box function to trivially return an empty rectangle.
+ results.get_animation_bounds_since =
+ base::Bind(&ReturnTrivialEmptyRectBound);
} else {
ApplyVisitor apply_visitor(traverse_list_, time_offset);
source_->Accept(&apply_visitor);
- return apply_visitor.animated();
+ results.animated = apply_visitor.animated();
+
+ // Setup a function for returning
+ results.get_animation_bounds_since = base::Bind(
+ &GetAnimationBoundsSince,
+ scoped_refptr<RefCountedTraversalList>(new RefCountedTraversalList(
+ apply_visitor.animated_traverse_list())),
+ time_offset, results.animated);
}
+ return results;
}
void AnimateNode::CommonInit(const Builder::InternalMap& node_animation_map,
diff --git a/src/cobalt/render_tree/animations/animate_node.h b/src/cobalt/render_tree/animations/animate_node.h
index b5a11e1..d0b81c8 100644
--- a/src/cobalt/render_tree/animations/animate_node.h
+++ b/src/cobalt/render_tree/animations/animate_node.h
@@ -22,6 +22,7 @@
#include "base/containers/small_map.h"
#include "base/memory/ref_counted.h"
+#include "cobalt/math/rect_f.h"
#include "cobalt/render_tree/animations/animation_list.h"
#include "cobalt/render_tree/movable.h"
#include "cobalt/render_tree/node.h"
@@ -139,10 +140,21 @@
return base::GetTypeId<AnimateNode>();
}
+ struct AnimateResults {
+ // The animated render tree, which is guaranteed to not contain any
+ // AnimateNodes.
+ scoped_refptr<Node> animated;
+
+ // Can be called in order to return a bounding rectangle around all
+ // nodes that are actively animated. The parameter specifies a "since"
+ // time. Any animations that had already expired *before* the "since" time
+ // will not be included in the returned bounding box.
+ // This information is likely to be used to enable optimizations where only
+ // regions of the screen that have changed are rendered.
+ base::Callback<math::RectF(base::TimeDelta)> get_animation_bounds_since;
+ };
// Apply the animations to the sub render tree with the given |time_offset|.
- // An animated sub-tree is returned, which is guaranteed to not contain any
- // AnimateNodes.
- scoped_refptr<Node> Apply(base::TimeDelta time_offset);
+ AnimateResults Apply(base::TimeDelta time_offset);
// Returns the sub-tree for which the animations apply to.
const scoped_refptr<Node> source() const { return source_; }
@@ -154,17 +166,6 @@
const base::TimeDelta& expiry() const { return expiry_; }
private:
- // A helper render tree visitor class used to compile sub render-tree
- // animations.
- class TraverseListBuilder;
- // A helper render tree visitor class used to apply compiled sub render-tree
- // animations. This class follows the traversal generated by
- // TraverseListBuilder.
- class ApplyVisitor;
-
- void CommonInit(const Builder::InternalMap& node_animation_map,
- const scoped_refptr<Node>& source);
-
// The compiled node animation list is a sequence of nodes that are either
// animated themselves, or on the path to an animated node. Only nodes in
// this sequence need to be traversed. TraverseListEntry is an entry in this
@@ -180,6 +181,27 @@
};
typedef std::vector<TraverseListEntry> TraverseList;
+ class RefCountedTraversalList;
+
+ // A helper render tree visitor class used to compile sub render-tree
+ // animations.
+ class TraverseListBuilder;
+ // A helper render tree visitor class used to apply compiled sub render-tree
+ // animations. This class follows the traversal generated by
+ // TraverseListBuilder.
+ class ApplyVisitor;
+ // A helper class to traverse an animated tree, and compute a bounding
+ // rectangle for all active animations.
+ class BoundsVisitor;
+
+ void CommonInit(const Builder::InternalMap& node_animation_map,
+ const scoped_refptr<Node>& source);
+
+ static math::RectF GetAnimationBoundsSince(
+ const scoped_refptr<RefCountedTraversalList>& traverse_list,
+ base::TimeDelta time_offset, const scoped_refptr<Node>& animated,
+ base::TimeDelta since);
+
// The compiled traversal list through the sub-tree represented by |source_|
// that guides us towards all nodes that need to be animated.
TraverseList traverse_list_;
diff --git a/src/cobalt/render_tree/animations/animate_node_test.cc b/src/cobalt/render_tree/animations/animate_node_test.cc
index f46e63e..0321bd8 100644
--- a/src/cobalt/render_tree/animations/animate_node_test.cc
+++ b/src/cobalt/render_tree/animations/animate_node_test.cc
@@ -48,12 +48,19 @@
template <typename T>
scoped_refptr<AnimateNode> CreateSingleAnimation(
const scoped_refptr<T>& target,
- typename Animation<T>::Function anim_function) {
+ typename Animation<T>::Function anim_function, base::TimeDelta expiry) {
AnimateNode::Builder animations_builder;
- animations_builder.Add(target, anim_function);
+ animations_builder.Add(target, anim_function, expiry);
return new AnimateNode(animations_builder, target);
}
+template <typename T>
+scoped_refptr<AnimateNode> CreateSingleAnimation(
+ const scoped_refptr<T>& target,
+ typename Animation<T>::Function anim_function) {
+ return CreateSingleAnimation(target, anim_function, base::TimeDelta::Max());
+}
+
class ImageFake : public Image {
public:
const math::Size& GetSize() const OVERRIDE { return size_; }
@@ -77,7 +84,7 @@
CreateSingleAnimation(text_node, base::Bind(&AnimateText));
scoped_refptr<Node> animated_render_tree =
- with_animations->Apply(base::TimeDelta::FromSeconds(1));
+ with_animations->Apply(base::TimeDelta::FromSeconds(1)).animated;
TextNode* animated_text_node =
dynamic_cast<TextNode*>(animated_render_tree.get());
EXPECT_TRUE(animated_text_node);
@@ -97,7 +104,7 @@
CreateSingleAnimation(image_node, base::Bind(&AnimateImage));
scoped_refptr<Node> animated_render_tree =
- with_animations->Apply(base::TimeDelta::FromSeconds(1));
+ with_animations->Apply(base::TimeDelta::FromSeconds(1)).animated;
ImageNode* animated_image_node =
dynamic_cast<ImageNode*>(animated_render_tree.get());
EXPECT_TRUE(animated_image_node);
@@ -118,7 +125,7 @@
CreateSingleAnimation(rect_node, base::Bind(&AnimateRect));
scoped_refptr<Node> animated_render_tree =
- with_animations->Apply(base::TimeDelta::FromSeconds(1));
+ with_animations->Apply(base::TimeDelta::FromSeconds(1)).animated;
RectNode* animated_rect_node =
dynamic_cast<RectNode*>(animated_render_tree.get());
EXPECT_TRUE(animated_rect_node);
@@ -146,21 +153,21 @@
ImageNode* animated_image_node;
animated_render_tree =
- with_animations->Apply(base::TimeDelta::FromSeconds(1));
+ with_animations->Apply(base::TimeDelta::FromSeconds(1)).animated;
animated_image_node = dynamic_cast<ImageNode*>(animated_render_tree.get());
EXPECT_TRUE(animated_image_node);
EXPECT_TRUE(animated_image_node);
EXPECT_FLOAT_EQ(3.0f, animated_image_node->data().destination_rect.width());
animated_render_tree =
- with_animations->Apply(base::TimeDelta::FromSeconds(2));
+ with_animations->Apply(base::TimeDelta::FromSeconds(2)).animated;
animated_image_node = dynamic_cast<ImageNode*>(animated_render_tree.get());
EXPECT_TRUE(animated_image_node);
EXPECT_TRUE(animated_image_node);
EXPECT_FLOAT_EQ(5.0f, animated_image_node->data().destination_rect.width());
animated_render_tree =
- with_animations->Apply(base::TimeDelta::FromSeconds(4));
+ with_animations->Apply(base::TimeDelta::FromSeconds(4)).animated;
animated_image_node = dynamic_cast<ImageNode*>(animated_render_tree.get());
EXPECT_TRUE(animated_image_node);
EXPECT_TRUE(animated_image_node);
@@ -198,7 +205,7 @@
new AnimateNode(animations_builder, image_node));
scoped_refptr<Node> animated_render_tree =
- with_animations->Apply(base::TimeDelta::FromSeconds(3));
+ with_animations->Apply(base::TimeDelta::FromSeconds(3)).animated;
ImageNode* animated_image_node =
dynamic_cast<ImageNode*>(animated_render_tree.get());
@@ -224,7 +231,7 @@
CreateSingleAnimation(transform_node, base::Bind(&AnimateTransform));
scoped_refptr<Node> animated_render_tree =
- with_animations->Apply(base::TimeDelta::FromSeconds(1));
+ with_animations->Apply(base::TimeDelta::FromSeconds(1)).animated;
MatrixTransformNode* animated_transform_node =
dynamic_cast<MatrixTransformNode*>(animated_render_tree.get());
@@ -256,7 +263,7 @@
new AnimateNode(animation_builder, composition_node);
scoped_refptr<Node> animated_render_tree =
- with_animations->Apply(base::TimeDelta::FromSeconds(1));
+ with_animations->Apply(base::TimeDelta::FromSeconds(1)).animated;
CompositionNode* animated_composition_node =
dynamic_cast<CompositionNode*>(animated_render_tree.get());
@@ -287,7 +294,7 @@
CreateSingleAnimation(transform_node, base::Bind(&AnimateTransform));
scoped_refptr<Node> animated_render_tree =
- with_animations->Apply(base::TimeDelta::FromSeconds(1));
+ with_animations->Apply(base::TimeDelta::FromSeconds(1)).animated;
MatrixTransformNode* animated_transform_node =
dynamic_cast<MatrixTransformNode*>(animated_render_tree.get());
@@ -324,7 +331,7 @@
new AnimateNode(transform_node_b);
scoped_refptr<Node> animated_render_tree =
- with_animations->Apply(base::TimeDelta::FromSeconds(1));
+ with_animations->Apply(base::TimeDelta::FromSeconds(1)).animated;
MatrixTransformNode* animated_transform_node =
dynamic_cast<MatrixTransformNode*>(animated_render_tree.get());
@@ -342,6 +349,159 @@
EXPECT_EQ(RectF(2.0f, 2.0f), animated_image_node->data().destination_rect);
}
+void BoundsAnimateRect(RectNode::Builder* rect_node,
+ base::TimeDelta time_elapsed) {
+ UNREFERENCED_PARAMETER(time_elapsed);
+ rect_node->rect = RectF(3.0f, 5.0f, 15.0f, 20.0f);
+}
+TEST(AnimateNodeTest, AnimationBounds) {
+ scoped_refptr<RectNode> rect_node_static(new RectNode(
+ RectF(1.0f, 1.0f),
+ scoped_ptr<Brush>(new SolidColorBrush(ColorRGBA(1.0f, 1.0f, 1.0f)))));
+
+ scoped_refptr<RectNode> rect_node_animated(new RectNode(
+ RectF(4.0f, 4.0f, 10.0f, 10.0f),
+ scoped_ptr<Brush>(new SolidColorBrush(ColorRGBA(1.0f, 1.0f, 1.0f)))));
+
+ CompositionNode::Builder builder;
+ builder.AddChild(rect_node_static);
+ builder.AddChild(rect_node_animated);
+ scoped_refptr<CompositionNode> composition(
+ new CompositionNode(builder.Pass()));
+
+ AnimateNode::Builder animations_builder;
+ animations_builder.Add(rect_node_animated, base::Bind(&BoundsAnimateRect));
+ scoped_refptr<AnimateNode> with_animations =
+ new AnimateNode(animations_builder, composition);
+
+ AnimateNode::AnimateResults results =
+ with_animations->Apply(base::TimeDelta::FromSeconds(1));
+
+ math::RectF animation_bounds =
+ results.get_animation_bounds_since.Run(base::TimeDelta());
+
+ EXPECT_EQ(RectF(3.0f, 5.0f, 15.0f, 20.0f), animation_bounds);
+}
+
+TEST(AnimateNodeTest, AnimationBoundsExpiration) {
+ scoped_refptr<RectNode> rect_node_static(new RectNode(
+ RectF(1.0f, 1.0f),
+ scoped_ptr<Brush>(new SolidColorBrush(ColorRGBA(1.0f, 1.0f, 1.0f)))));
+
+ scoped_refptr<RectNode> rect_node_animated(new RectNode(
+ RectF(4.0f, 4.0f, 10.0f, 10.0f),
+ scoped_ptr<Brush>(new SolidColorBrush(ColorRGBA(1.0f, 1.0f, 1.0f)))));
+
+ CompositionNode::Builder builder;
+ builder.AddChild(rect_node_static);
+ builder.AddChild(rect_node_animated);
+ scoped_refptr<CompositionNode> composition(
+ new CompositionNode(builder.Pass()));
+
+ AnimateNode::Builder animations_builder;
+ animations_builder.Add(rect_node_animated, base::Bind(&BoundsAnimateRect),
+ base::TimeDelta::FromSeconds(2));
+ scoped_refptr<AnimateNode> with_animations =
+ new AnimateNode(animations_builder, composition);
+
+ // Make sure that our animation bounds are updated when we apply animations
+ // before the expiration.
+ AnimateNode::AnimateResults results =
+ with_animations->Apply(base::TimeDelta::FromSeconds(1));
+ math::RectF animation_bounds =
+ results.get_animation_bounds_since.Run(base::TimeDelta::FromSeconds(0));
+ EXPECT_EQ(RectF(3.0f, 5.0f, 15.0f, 20.0f), animation_bounds);
+
+ // Make sure that our animation bounds are updated when we apply animations
+ // after the expiration, but we pass in a "since" value from before the
+ // animations expire.
+ results = with_animations->Apply(base::TimeDelta::FromSeconds(4));
+ animation_bounds =
+ results.get_animation_bounds_since.Run(base::TimeDelta::FromSeconds(1));
+ EXPECT_EQ(RectF(3.0f, 5.0f, 15.0f, 20.0f), animation_bounds);
+
+ // Make sure that our animation bounds are empty after our animations have
+ // expired.
+ results = with_animations->Apply(base::TimeDelta::FromSeconds(4));
+ animation_bounds =
+ results.get_animation_bounds_since.Run(base::TimeDelta::FromSeconds(3));
+ EXPECT_EQ(0, animation_bounds.size().GetArea());
+}
+
+void BoundsAnimateRect2(RectNode::Builder* rect_node,
+ base::TimeDelta time_elapsed) {
+ UNREFERENCED_PARAMETER(time_elapsed);
+ rect_node->rect = RectF(2.0f, 6.0f, 10.0f, 25.0f);
+}
+TEST(AnimateNodeTest, AnimationBoundsUnionsMultipleAnimations) {
+ scoped_refptr<RectNode> rect_node_1(new RectNode(
+ RectF(1.0f, 1.0f),
+ scoped_ptr<Brush>(new SolidColorBrush(ColorRGBA(1.0f, 1.0f, 1.0f)))));
+
+ scoped_refptr<RectNode> rect_node_2(new RectNode(
+ RectF(4.0f, 4.0f, 10.0f, 10.0f),
+ scoped_ptr<Brush>(new SolidColorBrush(ColorRGBA(1.0f, 1.0f, 1.0f)))));
+
+ CompositionNode::Builder builder;
+ builder.AddChild(rect_node_1);
+ builder.AddChild(rect_node_2);
+ scoped_refptr<CompositionNode> composition(
+ new CompositionNode(builder.Pass()));
+
+ AnimateNode::Builder animations_builder;
+ animations_builder.Add(rect_node_1, base::Bind(&BoundsAnimateRect));
+ animations_builder.Add(rect_node_2, base::Bind(&BoundsAnimateRect2));
+ scoped_refptr<AnimateNode> with_animations =
+ new AnimateNode(animations_builder, composition);
+
+ // Make sure that our animation bounds are the union from our two animated
+ // resulting boxes.
+ AnimateNode::AnimateResults results =
+ with_animations->Apply(base::TimeDelta::FromSeconds(1));
+ math::RectF animation_bounds =
+ results.get_animation_bounds_since.Run(base::TimeDelta::FromSeconds(0));
+ EXPECT_EQ(RectF(2.0f, 5.0f, 16.0f, 26.0f), animation_bounds);
+}
+
+void AnimateTranslate(CompositionNode::Builder* composition_node,
+ base::TimeDelta time_elapsed) {
+ UNREFERENCED_PARAMETER(time_elapsed);
+ composition_node->set_offset(math::Vector2dF(4.0f, 4.0f));
+}
+// This test makes sure that the animation bounds are calculated correctly when
+// multiple nodes on the path from the root to a leaf node are animated. Our
+// results should use the *animated* path to the node to calculate the bounds,
+// versus the non-animated path.
+TEST(AnimateNodeTest, AnimationBoundsWorksForCompoundedTransformations) {
+ scoped_refptr<RectNode> rect_node(new RectNode(
+ RectF(8.0f, 8.0f),
+ scoped_ptr<Brush>(new SolidColorBrush(ColorRGBA(1.0f, 1.0f, 1.0f)))));
+
+ CompositionNode::Builder composition_node_builder_1;
+ composition_node_builder_1.AddChild(rect_node);
+ scoped_refptr<CompositionNode> composition_node_1(
+ new CompositionNode(composition_node_builder_1.Pass()));
+
+ CompositionNode::Builder composition_node_builder_2;
+ composition_node_builder_2.AddChild(composition_node_1);
+ scoped_refptr<CompositionNode> composition_node_2(
+ new CompositionNode(composition_node_builder_2.Pass()));
+
+ AnimateNode::Builder animations_builder;
+ animations_builder.Add(composition_node_1, base::Bind(&AnimateTranslate));
+ animations_builder.Add(composition_node_2, base::Bind(&AnimateTranslate));
+ scoped_refptr<AnimateNode> with_animations =
+ new AnimateNode(animations_builder, composition_node_2);
+
+ // Make sure that our animation bounds are the union from our two animated
+ // resulting boxes.
+ AnimateNode::AnimateResults results =
+ with_animations->Apply(base::TimeDelta::FromSeconds(1));
+ math::RectF animation_bounds =
+ results.get_animation_bounds_since.Run(base::TimeDelta::FromSeconds(0));
+ EXPECT_EQ(RectF(8.0f, 8.0f, 8.0f, 8.0f), animation_bounds);
+}
+
} // namespace animations
} // namespace render_tree
} // namespace cobalt
diff --git a/src/cobalt/render_tree/brush.h b/src/cobalt/render_tree/brush.h
index d98eeac..b81b95e 100644
--- a/src/cobalt/render_tree/brush.h
+++ b/src/cobalt/render_tree/brush.h
@@ -107,6 +107,12 @@
// and destination points.
const ColorStopList& color_stops() const { return color_stops_; }
+ // Returns true if, and only if the brush is horizontal.
+ bool IsHorizontal() const { return (source_.y() == dest_.y()); }
+
+ // Returns true if, and only if the brush is vertical.
+ bool IsVertical() const { return (source_.x() == dest_.x()); }
+
private:
math::PointF source_;
math::PointF dest_;
diff --git a/src/cobalt/render_tree/color_rgba.h b/src/cobalt/render_tree/color_rgba.h
index ee5dbe9..f27f24c 100644
--- a/src/cobalt/render_tree/color_rgba.h
+++ b/src/cobalt/render_tree/color_rgba.h
@@ -22,10 +22,21 @@
namespace cobalt {
namespace render_tree {
+// Note: this does not handle infinities or nans.
+inline float clamp(float input, float min, float max) {
+#if defined(STARBOARD)
+ if (SB_UNLIKELY(input < min)) return min;
+ if (SB_UNLIKELY(input > max)) return max;
+#else
+ if (input < min) return min;
+ if (input > max) return max;
+#endif
+ return input;
+}
+
// Used to specify a color in the RGB (plus alpha) space.
// This color format is referenced by many render_tree objects in order to
-// specify a color. It is expressed by a float for each component, and must
-// be in the range [0.0, 1.0].
+// specify a color.
struct ColorRGBA {
public:
ColorRGBA() : r_(0), g_(0), b_(0), a_(0) {}
@@ -81,13 +92,76 @@
a_ = value;
}
- float r() const { return r_; }
- float g() const { return g_; }
- float b() const { return b_; }
- float a() const { return a_; }
+ // These functions clamp the color channel values between 0.0f and 1.0f.
+ float r() const { return clamp(r_, 0.0f, 1.0f); }
+ float g() const { return clamp(g_, 0.0f, 1.0f); }
+ float b() const { return clamp(b_, 0.0f, 1.0f); }
+ float a() const { return clamp(a_, 0.0f, 1.0f); }
+
+ uint8_t rgb8_r() const { return static_cast<uint8_t>(r() * 255); }
+
+ uint8_t rgb8_g() const { return static_cast<uint8_t>(g() * 255); }
+
+ uint8_t rgb8_b() const { return static_cast<uint8_t>(b() * 255); }
+
+ uint8_t rgb8_a() const { return static_cast<uint8_t>(a() * 255); }
+
+ ColorRGBA& operator=(const ColorRGBA& other) {
+ r_ = other.r_;
+ g_ = other.g_;
+ b_ = other.b_;
+ a_ = other.a_;
+ return *this;
+ }
+
+ ColorRGBA& operator-=(const ColorRGBA& other) {
+ r_ -= other.r_;
+ g_ -= other.g_;
+ b_ -= other.b_;
+ a_ -= other.a_;
+ return *this;
+ }
+
+ ColorRGBA& operator+=(const ColorRGBA& other) {
+ r_ += other.r_;
+ g_ += other.g_;
+ b_ += other.b_;
+ a_ += other.a_;
+ return *this;
+ }
+
+ ColorRGBA& operator+=(const float f) {
+ r_ += f;
+ b_ += f;
+ g_ += f;
+ a_ += f;
+ return *this;
+ }
+
+ ColorRGBA& operator-=(const float f) {
+ r_ -= f;
+ b_ -= f;
+ g_ -= f;
+ a_ -= f;
+ return *this;
+ }
+
+ ColorRGBA& operator*=(const float f) {
+ r_ *= f;
+ b_ *= f;
+ g_ *= f;
+ a_ *= f;
+ return *this;
+ }
+
+ ColorRGBA& operator/=(const float f) {
+ float one_over_f(1.0f / f);
+ *this *= one_over_f;
+ return *this;
+ }
private:
- void CheckRange(float value) {
+ void CheckRange(float value) const {
DCHECK_LE(0.0f, value);
DCHECK_GE(1.0f, value);
}
@@ -100,6 +174,46 @@
lhs.a() == rhs.a();
}
+inline bool operator!=(const ColorRGBA& lhs, const ColorRGBA& rhs) {
+ return !(lhs == rhs);
+}
+
+inline ColorRGBA operator-(const ColorRGBA& lhs, const ColorRGBA& rhs) {
+ ColorRGBA color(lhs);
+ color -= rhs;
+ return color;
+}
+
+inline ColorRGBA operator+(const ColorRGBA& lhs, const ColorRGBA& rhs) {
+ ColorRGBA color(lhs);
+ color += rhs;
+ return color;
+}
+
+inline ColorRGBA operator+(const ColorRGBA& lhs, const float rhs) {
+ ColorRGBA color(lhs);
+ color += rhs;
+ return color;
+}
+
+inline ColorRGBA operator-(const ColorRGBA& lhs, const float rhs) {
+ ColorRGBA color(lhs);
+ color -= rhs;
+ return color;
+}
+
+inline ColorRGBA operator*(const ColorRGBA& lhs, const float rhs) {
+ ColorRGBA color(lhs);
+ color *= rhs;
+ return color;
+}
+
+inline ColorRGBA operator/(const ColorRGBA& lhs, const float rhs) {
+ ColorRGBA color(lhs);
+ color /= rhs;
+ return color;
+}
+
// Used by tests.
inline std::ostream& operator<<(std::ostream& stream, const ColorRGBA& color) {
return stream << "rgba(" << color.r() << ", " << color.g() << ", "
diff --git a/src/cobalt/renderer/pipeline.cc b/src/cobalt/renderer/pipeline.cc
index 9974067..0056361 100644
--- a/src/cobalt/renderer/pipeline.cc
+++ b/src/cobalt/renderer/pipeline.cc
@@ -128,6 +128,7 @@
// must be destroyed before we shutdown the rasterizer thread since it may
// contain references to render tree nodes and resources.
last_rendered_expired_render_tree_ = NULL;
+ last_render_tree_ = NULL;
// Submit a shutdown task to the rasterizer thread so that it can shutdown
// anything that must be shutdown from that thread.
@@ -265,19 +266,45 @@
const scoped_refptr<backend::RenderTarget>& render_target) {
TRACE_EVENT0("cobalt::renderer",
"Pipeline::RasterizeSubmissionToRenderTarget()");
+
+ // Keep track of the last render tree that we rendered so that we can watch
+ // if it changes, in which case we should reset our tracked
+ // |previous_animated_area_|.
+ if (submission.render_tree != last_render_tree_) {
+ last_render_tree_ = submission.render_tree;
+ previous_animated_area_ = base::nullopt;
+ last_render_time_ = base::nullopt;
+ }
+
// Animate the render tree using the submitted animations.
render_tree::animations::AnimateNode* animate_node =
base::polymorphic_downcast<render_tree::animations::AnimateNode*>(
submission.render_tree.get());
- scoped_refptr<Node> animated_render_tree =
+ render_tree::animations::AnimateNode::AnimateResults results =
animate_node->Apply(submission.time_offset);
+ // Calculate a bounding box around the active animations. Union it with the
+ // bounding box around active animations from the previous frame, and we get
+ // a scissor rectangle marking the dirty regions of the screen.
+ math::RectF animated_bounds = results.get_animation_bounds_since.Run(
+ last_render_time_ ? *last_render_time_ : base::TimeDelta());
+ math::Rect rounded_bounds = math::RoundOut(animated_bounds);
+ base::optional<math::Rect> redraw_area;
+ if (previous_animated_area_) {
+ redraw_area = math::UnionRects(rounded_bounds, *previous_animated_area_);
+ }
+ previous_animated_area_ = rounded_bounds;
+
// Rasterize the animated render tree.
- rasterizer_->Submit(animated_render_tree, render_target);
+ rasterizer::Rasterizer::Options rasterizer_options;
+ rasterizer_options.dirty = redraw_area;
+ rasterizer_->Submit(results.animated, render_target, rasterizer_options);
if (!submission.on_rasterized_callback.is_null()) {
submission.on_rasterized_callback.Run();
}
+
+ last_render_time_ = submission.time_offset;
}
void Pipeline::InitializeRasterizerThread(
@@ -352,11 +379,10 @@
render_tree::animations::AnimateNode* animate_node =
base::polymorphic_downcast<render_tree::animations::AnimateNode*>(
submission.render_tree.get());
- scoped_refptr<Node> animated_render_tree =
+ render_tree::animations::AnimateNode::AnimateResults results =
animate_node->Apply(submission.time_offset);
- std::string tree_dump =
- render_tree::DumpRenderTreeToString(animated_render_tree);
+ std::string tree_dump = render_tree::DumpRenderTreeToString(results.animated);
if (message.empty() || message == "undefined") {
// If no filename was specified, send output to the console.
LOG(INFO) << tree_dump.c_str();
diff --git a/src/cobalt/renderer/pipeline.h b/src/cobalt/renderer/pipeline.h
index 975f593..3b96cc3 100644
--- a/src/cobalt/renderer/pipeline.h
+++ b/src/cobalt/renderer/pipeline.h
@@ -176,6 +176,15 @@
// frame, even if it hasn't changed.
const bool submit_even_if_render_tree_is_unchanged_;
+ // Keeps track of the last rendered animated render tree.
+ scoped_refptr<render_tree::Node> last_render_tree_;
+ // Keeps track of the area of the screen that animations previously existed
+ // within, so that we can know which regions of the screens would be dirty
+ // next frame.
+ base::optional<math::Rect> previous_animated_area_;
+ // The submission time used during the last render tree render.
+ base::optional<base::TimeDelta> last_render_time_;
+
// Timers for tracking how frequently |RasterizeCurrentTree| is called and
// the amount of time spent in |RasterizeCurrentTree| each call.
base::CValTimeIntervalTimer<base::CValPublic>
diff --git a/src/cobalt/renderer/pipeline_test.cc b/src/cobalt/renderer/pipeline_test.cc
index c258641..c70d44e 100644
--- a/src/cobalt/renderer/pipeline_test.cc
+++ b/src/cobalt/renderer/pipeline_test.cc
@@ -52,7 +52,7 @@
void Submit(const scoped_refptr<cobalt::render_tree::Node>&,
const scoped_refptr<cobalt::renderer::backend::RenderTarget>&,
- int options) OVERRIDE {
+ const Options& options) OVERRIDE {
if (last_submission_time) {
// Simulate a "wait for vsync".
base::TimeDelta since_last_submit =
diff --git a/src/cobalt/renderer/rasterizer/benchmark.cc b/src/cobalt/renderer/rasterizer/benchmark.cc
index 5507cb7..c0d2567 100644
--- a/src/cobalt/renderer/rasterizer/benchmark.cc
+++ b/src/cobalt/renderer/rasterizer/benchmark.cc
@@ -116,8 +116,10 @@
for (int i = 0; i < kRenderIterationCount; ++i) {
AnimateNode* animate_node =
base::polymorphic_downcast<AnimateNode*>(scene.get());
- scoped_refptr<Node> animated = animate_node->Apply(
- base::TimeDelta::FromSecondsD(i * kFixedTimeStepInSecondsPerFrame));
+ scoped_refptr<Node> animated =
+ animate_node->Apply(base::TimeDelta::FromSecondsD(
+ i * kFixedTimeStepInSecondsPerFrame))
+ .animated;
// Submit the render tree to be rendered.
rasterizer->Submit(animated, test_surface);
diff --git a/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.cc b/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.cc
index 7739988..bcb25db 100644
--- a/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.cc
@@ -45,7 +45,11 @@
software_rasterizer_(0),
cache_memory_usage_(
"Memory.CachedSoftwareRasterizer.CacheUsage", 0,
- "Total memory occupied by cached software-rasterized surfaces.") {}
+ "Total memory occupied by cached software-rasterized surfaces."),
+ cache_frame_usage_(
+ "Memory.CachedSoftwareRasterizer.FrameCacheUsage", 0,
+ "Total memory occupied by cache software-rasterizer surfaces that "
+ "were referenced this frame.") {}
CachedSoftwareRasterizer::~CachedSoftwareRasterizer() {
// Clean up any leftover surfaces.
@@ -62,19 +66,35 @@
CacheMap::iterator current = iter;
++iter;
- // If the surface wasn't referenced, destroy it. If it was, mark it as
- // being unreferenced.
- if (!current->second.referenced) {
- cache_memory_usage_ -= current->second.GetEstimatedMemoryUsage();
- SbBlitterDestroySurface(current->second.surface);
- surface_map_.erase(current);
- } else {
+ // If the surface was referenced mark it as being unreferenced for the next
+ // frame.
+ if (current->second.referenced) {
#if defined(ENABLE_DEBUG_CONSOLE)
current->second.created = false;
#endif // defined(ENABLE_DEBUG_CONSOLE)
current->second.referenced = false;
}
}
+
+ // Reset our current frame cache usage to 0 since this is the start of a new
+ // frame.
+ cache_frame_usage_ = 0;
+}
+
+void CachedSoftwareRasterizer::PurgeUntilSpaceAvailable(int space_needed) {
+ while (space_needed + cache_memory_usage_.value() > cache_capacity_) {
+ CacheMap::iterator next_item = surface_map_.begin();
+
+ // We shouldn't call this function if it means we would have to purge
+ // elements that were referenced this frame. This is to avoid thrashing
+ // the cache, we would prefer to cache as much as we can in a frame, and
+ // then just not cache whatever we can't without cycling what's in the cache
+ // already.
+ DCHECK(!next_item->second.referenced);
+ cache_memory_usage_ -= next_item->second.GetEstimatedMemoryUsage();
+ SbBlitterDestroySurface(next_item->second.surface);
+ surface_map_.erase(next_item);
+ }
}
CachedSoftwareRasterizer::Surface CachedSoftwareRasterizer::GetSurface(
@@ -88,8 +108,18 @@
if (found->second.scale.x() == transform.scale().x() &&
found->second.scale.y() == transform.scale().y()) {
#endif
- found->second.referenced = true;
- return found->second;
+ std::pair<render_tree::Node*, Surface> to_insert =
+ std::make_pair(node, found->second);
+
+ // Move this surface's position in the queue to the front, since it was
+ // referenced.
+ to_insert.second.referenced = true;
+ surface_map_.erase(found);
+ surface_map_.insert(to_insert);
+
+ cache_frame_usage_ += found->second.GetEstimatedMemoryUsage();
+
+ return to_insert.second;
}
}
@@ -191,17 +221,22 @@
software_surface.surface =
SbBlitterCreateSurfaceFromPixelData(device_, pixel_data);
- if (software_surface.GetEstimatedMemoryUsage() +
- cache_memory_usage_.value() <=
+ if (software_surface.GetEstimatedMemoryUsage() + cache_frame_usage_.value() <=
cache_capacity_) {
software_surface.cached = true;
+ cache_frame_usage_ += software_surface.GetEstimatedMemoryUsage();
+
if (found != surface_map_.end()) {
+ // This surface may have already been in the cache if it was in there
+ // with a different scale. In that case, replace the old one.
cache_memory_usage_ -= found->second.GetEstimatedMemoryUsage();
- found->second = software_surface;
- } else {
- std::pair<CacheMap::iterator, bool> inserted =
- surface_map_.insert(std::make_pair(node, software_surface));
+ surface_map_.erase(found);
}
+
+ PurgeUntilSpaceAvailable(software_surface.GetEstimatedMemoryUsage());
+
+ std::pair<CacheMap::iterator, bool> inserted =
+ surface_map_.insert(std::make_pair(node, software_surface));
cache_memory_usage_ += software_surface.GetEstimatedMemoryUsage();
}
diff --git a/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.h b/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.h
index f71d49b..10d76e9 100644
--- a/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/blitter/cached_software_rasterizer.h
@@ -17,6 +17,7 @@
#ifndef COBALT_RENDERER_RASTERIZER_BLITTER_CACHED_SOFTWARE_RASTERIZER_H_
#define COBALT_RENDERER_RASTERIZER_BLITTER_CACHED_SOFTWARE_RASTERIZER_H_
+#include "base/containers/linked_hash_map.h"
#include "base/hash_tables.h"
#include "base/memory/ref_counted.h"
#include "cobalt/base/c_val.h"
@@ -116,8 +117,15 @@
}
private:
+ typedef base::linked_hash_map<render_tree::Node*, Surface> CacheMap;
+
+ // Release surfaces until we have |space_needed| free bytes in the cache.
+ // This function will never release surfaces that were referenced this frame.
+ // It is an error to call this function if it is impossible to purge
+ // unreferenced surfaces until the desired amount of free space is available.
+ void PurgeUntilSpaceAvailable(int space_needed);
+
// The cache, mapping input render_tree::Node references to cached surfaces.
- typedef base::hash_map<render_tree::Node*, Surface> CacheMap;
CacheMap surface_map_;
const int cache_capacity_;
@@ -133,6 +141,9 @@
// The amount of memory currently consumed by the surfaces populating the
// cache.
base::CVal<base::cval::SizeInBytes> cache_memory_usage_;
+
+ // Cache memory used this frame only.
+ base::CVal<base::cval::SizeInBytes> cache_frame_usage_;
};
} // namespace blitter
diff --git a/src/cobalt/renderer/rasterizer/blitter/cobalt_blitter_conversions.cc b/src/cobalt/renderer/rasterizer/blitter/cobalt_blitter_conversions.cc
index 7206ba2..29a960a 100644
--- a/src/cobalt/renderer/rasterizer/blitter/cobalt_blitter_conversions.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/cobalt_blitter_conversions.cc
@@ -23,11 +23,9 @@
namespace rasterizer {
namespace blitter {
-namespace {
int RoundToInt(float value) {
return static_cast<int>(std::floor(value + 0.5f));
}
-} // namespace
math::Rect RectFToRect(const math::RectF& rectf) {
// We convert from floating point to integer in such a way that two boxes
diff --git a/src/cobalt/renderer/rasterizer/blitter/cobalt_blitter_conversions.h b/src/cobalt/renderer/rasterizer/blitter/cobalt_blitter_conversions.h
index 29afcec..d13b77d 100644
--- a/src/cobalt/renderer/rasterizer/blitter/cobalt_blitter_conversions.h
+++ b/src/cobalt/renderer/rasterizer/blitter/cobalt_blitter_conversions.h
@@ -28,6 +28,7 @@
namespace rasterizer {
namespace blitter {
+int RoundToInt(float value);
math::Rect RectFToRect(const math::RectF& rectf);
inline SbBlitterRect RectToBlitterRect(const math::Rect& rect) {
diff --git a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc
index dee6372..7c9304e 100644
--- a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.cc
@@ -51,7 +51,7 @@
void Submit(const scoped_refptr<render_tree::Node>& render_tree,
const scoped_refptr<backend::RenderTarget>& render_target,
- int options);
+ const Options& options);
render_tree::ResourceProvider* GetResourceProvider();
@@ -59,6 +59,7 @@
#if defined(ENABLE_DEBUG_CONSOLE)
void OnToggleHighlightSoftwareDraws(const std::string& message);
#endif
+ void SetupLastFrameSurface(int width, int height);
base::ThreadChecker thread_checker_;
@@ -68,6 +69,11 @@
int64 submit_count_;
+ // We maintain a "final results" surface that mirrors the display buffer.
+ // This way, we can rerender only the dirty parts of the screen to this
+ // |current_frame_| buffer and then blit that to the display.
+ SbBlitterSurface current_frame_;
+
ScratchSurfaceCache scratch_surface_cache_;
base::optional<SurfaceCacheDelegate> surface_cache_delegate_;
base::optional<common::SurfaceCache> surface_cache_;
@@ -90,6 +96,7 @@
: context_(base::polymorphic_downcast<backend::GraphicsContextBlitter*>(
graphics_context)),
submit_count_(0),
+ current_frame_(kSbBlitterInvalidSurface),
scratch_surface_cache_(context_->GetSbBlitterDevice(),
context_->GetSbBlitterContext(),
scratch_surface_size_in_bytes),
@@ -122,7 +129,7 @@
}
}
-HardwareRasterizer::Impl::~Impl() {}
+HardwareRasterizer::Impl::~Impl() { SbBlitterDestroySurface(current_frame_); }
#if defined(ENABLE_DEBUG_CONSOLE)
void HardwareRasterizer::Impl::OnToggleHighlightSoftwareDraws(
@@ -134,7 +141,8 @@
void HardwareRasterizer::Impl::Submit(
const scoped_refptr<render_tree::Node>& render_tree,
- const scoped_refptr<backend::RenderTarget>& render_target, int options) {
+ const scoped_refptr<backend::RenderTarget>& render_target,
+ const Options& options) {
TRACE_EVENT0("cobalt::renderer", "Rasterizer::Submit()");
int width = render_target->GetSize().width();
@@ -146,8 +154,12 @@
base::polymorphic_downcast<backend::RenderTargetBlitter*>(
render_target.get());
- CHECK(SbBlitterSetRenderTarget(context,
- render_target_blitter->GetSbRenderTarget()));
+ if (!SbBlitterIsSurfaceValid(current_frame_)) {
+ SetupLastFrameSurface(width, height);
+ }
+
+ CHECK(SbBlitterSetRenderTarget(
+ context, SbBlitterGetRenderTargetFromSurface(current_frame_)));
// Update our surface cache to do per-frame calculations such as deciding
// which render tree nodes are candidates for caching in this upcoming
@@ -163,7 +175,9 @@
// purposes, so that despite the Blitter API implementation, we ensure that
// if the output buffer is not completely rendered to, data from a previous
// process cannot leak in.
- if (options & Rasterizer::kSubmitOptions_Clear || submit_count_ < 3) {
+ bool cleared = false;
+ if (options.flags & Rasterizer::kSubmitFlags_Clear || submit_count_ < 3) {
+ cleared = true;
CHECK(SbBlitterSetBlending(context, false));
CHECK(SbBlitterSetColor(context, SbBlitterColorFromRGBA(0, 0, 0, 0)));
CHECK(SbBlitterFillRect(context, SbBlitterMakeRect(0, 0, width, height)));
@@ -173,10 +187,16 @@
TRACE_EVENT0("cobalt::renderer", "VisitRenderTree");
// Visit the render tree with our Blitter API visitor.
+ BoundsStack start_bounds(context_->GetSbBlitterContext(),
+ math::Rect(render_target_blitter->GetSize()));
+ if (options.dirty && !cleared) {
+ // If a dirty rectangle was specified, limit our redrawing to within it.
+ start_bounds.Push(*options.dirty);
+ }
+
RenderState initial_render_state(
- render_target_blitter->GetSbRenderTarget(), Transform(),
- BoundsStack(context_->GetSbBlitterContext(),
- math::Rect(render_target_blitter->GetSize())));
+ SbBlitterGetRenderTargetFromSurface(current_frame_), Transform(),
+ start_bounds);
#if defined(ENABLE_DEBUG_CONSOLE)
initial_render_state.highlight_software_draws =
toggle_highlight_software_draws_;
@@ -191,6 +211,13 @@
}
// Finally flip the surface to make visible the rendered results.
+ CHECK(SbBlitterSetRenderTarget(context,
+ render_target_blitter->GetSbRenderTarget()));
+ CHECK(SbBlitterSetBlending(context, false));
+ CHECK(SbBlitterSetModulateBlitsWithColor(context, false));
+ CHECK(SbBlitterBlitRectToRect(context, current_frame_,
+ SbBlitterMakeRect(0, 0, width, height),
+ SbBlitterMakeRect(0, 0, width, height)));
CHECK(SbBlitterFlushContext(context));
render_target_blitter->Flip();
@@ -201,6 +228,12 @@
return resource_provider_.get();
}
+void HardwareRasterizer::Impl::SetupLastFrameSurface(int width, int height) {
+ current_frame_ =
+ SbBlitterCreateRenderTargetSurface(context_->GetSbBlitterDevice(), width,
+ height, kSbBlitterSurfaceFormatRGBA8);
+}
+
HardwareRasterizer::HardwareRasterizer(
backend::GraphicsContext* graphics_context,
int scratch_surface_size_in_bytes, int surface_cache_size_in_bytes,
@@ -213,7 +246,8 @@
void HardwareRasterizer::Submit(
const scoped_refptr<render_tree::Node>& render_tree,
- const scoped_refptr<backend::RenderTarget>& render_target, int options) {
+ const scoped_refptr<backend::RenderTarget>& render_target,
+ const Options& options) {
return impl_->Submit(render_tree, render_target, options);
}
diff --git a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h
index 6a48124..5ab8fd5 100644
--- a/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/blitter/hardware_rasterizer.h
@@ -47,7 +47,7 @@
// into the constructor.
void Submit(const scoped_refptr<render_tree::Node>& render_tree,
const scoped_refptr<backend::RenderTarget>& render_target,
- int options) OVERRIDE;
+ const Options& options) OVERRIDE;
render_tree::ResourceProvider* GetResourceProvider() OVERRIDE;
diff --git a/src/cobalt/renderer/rasterizer/blitter/linear_gradient.cc b/src/cobalt/renderer/rasterizer/blitter/linear_gradient.cc
new file mode 100644
index 0000000..1dc7e96
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/blitter/linear_gradient.cc
@@ -0,0 +1,311 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/renderer/rasterizer/blitter/linear_gradient.h"
+
+#include <algorithm>
+
+#include "cobalt/base/polymorphic_downcast.h"
+#include "cobalt/renderer/rasterizer/blitter/cobalt_blitter_conversions.h"
+#include "cobalt/renderer/rasterizer/blitter/render_state.h"
+#include "cobalt/renderer/rasterizer/blitter/skia_blitter_conversions.h"
+#include "cobalt/renderer/rasterizer/skia/conversions.h"
+
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+#include "third_party/skia/include/core/SkShader.h"
+#include "third_party/skia/include/effects/SkGradientShader.h"
+
+#if SB_HAS(BLITTER)
+
+namespace {
+
+using cobalt::render_tree::ColorRGBA;
+using cobalt::render_tree::ColorStopList;
+using cobalt::render_tree::LinearGradientBrush;
+using cobalt::renderer::rasterizer::blitter::RenderState;
+using cobalt::renderer::rasterizer::blitter::SkiaToBlitterPixelFormat;
+using cobalt::renderer::rasterizer::blitter::RectFToRect;
+using cobalt::renderer::rasterizer::skia::SkiaColorStops;
+
+int GetPixelOffsetInBytes(const SkImageInfo& image_info,
+ const SbBlitterPixelData& pixel_data) {
+ if (image_info.width() == 1)
+ return SbBlitterGetPixelDataPitchInBytes(pixel_data);
+
+ SbBlitterPixelDataFormat pixel_format =
+ SkiaToBlitterPixelFormat(image_info.colorType());
+ return SbBlitterBytesPerPixelForFormat(pixel_format);
+}
+
+void ColorStopToPixelData(SbBlitterPixelDataFormat pixel_format,
+ const ColorRGBA& color_stop, uint8_t* pixel_data_out,
+ uint8_t* pixel_data_out_end) {
+ DCHECK(pixel_data_out_end >=
+ (pixel_data_out + SbBlitterBytesPerPixelForFormat(pixel_format)));
+ uint8_t final_b(color_stop.rgb8_b());
+ uint8_t final_g(color_stop.rgb8_g());
+ uint8_t final_r(color_stop.rgb8_r());
+ uint8_t final_a(color_stop.rgb8_a());
+
+ switch (pixel_format) {
+ case kSbBlitterPixelDataFormatARGB8:
+ pixel_data_out[0] = final_a;
+ pixel_data_out[1] = final_r;
+ pixel_data_out[2] = final_g;
+ pixel_data_out[3] = final_b;
+ break;
+ case kSbBlitterPixelDataFormatBGRA8:
+ pixel_data_out[0] = final_b;
+ pixel_data_out[1] = final_g;
+ pixel_data_out[2] = final_r;
+ pixel_data_out[3] = final_a;
+ break;
+ case kSbBlitterPixelDataFormatRGBA8:
+ pixel_data_out[0] = final_r;
+ pixel_data_out[1] = final_g;
+ pixel_data_out[2] = final_b;
+ pixel_data_out[3] = final_a;
+ break;
+ default:
+ NOTREACHED() << "Unknown or unsupported pixel format." << pixel_format;
+ break;
+ }
+}
+
+// This function uses Skia to render a gradient into the pixel data pointed
+// at pixel_data_begin. This function is intended as a fallback in case when
+// RenderOptimizedLinearGradient is unable to render the gradient.
+void RenderComplexLinearGradient(const LinearGradientBrush& brush,
+ const ColorStopList& color_stops, int width,
+ int height, const SkImageInfo& image_info,
+ uint8_t* pixel_data_begin,
+ int pitch_in_bytes) {
+ SkBitmap bitmap;
+ bitmap.installPixels(image_info, pixel_data_begin, pitch_in_bytes);
+ SkCanvas canvas(bitmap);
+ canvas.clear(SkColorSetARGB(0, 0, 0, 0));
+
+ SkPoint points[2];
+ points[0].fX = 0;
+ points[0].fY = 0;
+ points[1].fX = 0;
+ points[1].fY = 0;
+
+ // The -1 offset is easiest to think about in a 2 pixel case:
+ // If width = 2, then the ending coordinate should be 1.
+ if (brush.IsHorizontal()) {
+ if (brush.dest().x() < brush.source().x()) {
+ points[0].fX = width - 1; // Right to Left.
+ } else {
+ points[1].fX = width - 1; // Left to Right.
+ }
+ } else {
+ if (brush.dest().y() < brush.source().y()) {
+ points[0].fY = height - 1; // High to Low.
+ } else {
+ points[1].fY = height - 1; // Low to High.
+ }
+ }
+
+ SkiaColorStops skia_color_stops(color_stops);
+ SkAutoTUnref<SkShader> shader(SkGradientShader::CreateLinear(
+ points, skia_color_stops.colors.data(), skia_color_stops.positions.data(),
+ skia_color_stops.size(), SkShader::kClamp_TileMode,
+ SkGradientShader::kInterpolateColorsInPremul_Flag, NULL));
+ SkPaint paint;
+ paint.setShader(shader);
+ canvas.drawPaint(paint);
+}
+
+// This function tries to render a "simple" gradient into the pixel data pointed
+// to in the range [pixel_data_begin, pixel_data_end). Simple gradients here
+// are defined as has following properties:
+// 1. Vertical or horizontal gradient.
+// 2. Two color stops only -- one at position 0, and one at position 1.
+//
+// Having these conditions greatly simplifies the logic, and allows us to avoid
+// the overhead of dealing with Skia.
+//
+// Returns: true if it could successfully render, false if optimal conditions
+// are not met. If false is returned, nothing is written to the pixel data
+// buffer.
+bool RenderSimpleGradient(const LinearGradientBrush& brush,
+ const ColorStopList& color_stops, int width,
+ int height, SbBlitterPixelDataFormat pixel_format,
+ uint8_t* pixel_data_begin, uint8_t* pixel_data_end,
+ int pixel_offset_in_bytes) {
+ if (color_stops.size() != 2) {
+ return false;
+ }
+
+ const float small_value(1E-6);
+ if ((color_stops[0].position < -small_value) ||
+ (color_stops[0].position > small_value)) {
+ return false;
+ }
+ if ((color_stops[1].position < 1 - small_value) ||
+ (color_stops[1].position > 1 + small_value)) {
+ return false;
+ }
+
+ const int number_pixels = width * height;
+
+ if ((number_pixels < 2) || (width < 0) || (height < 0)) {
+ return false;
+ }
+
+ ColorRGBA start_color = color_stops[0].color;
+ ColorRGBA end_color = color_stops[1].color;
+
+ // Swap start and end colors if the gradient direction is right to left or
+ // down to up.
+ if (brush.IsHorizontal()) {
+ if (brush.dest().x() < brush.source().x()) {
+ std::swap(start_color, end_color);
+ }
+ } else {
+ if (brush.dest().y() < brush.source().y()) {
+ std::swap(start_color, end_color);
+ }
+ }
+
+ ColorRGBA color_step((end_color - start_color) / (number_pixels - 1));
+ // We add the color_stop * 0.5f to match pixel positioning in software Skia.
+ ColorRGBA current_color(start_color + color_step * 0.5f);
+ ColorStopToPixelData(pixel_format, current_color, pixel_data_begin,
+ pixel_data_end);
+
+ uint8_t* second_pixel = pixel_data_begin + pixel_offset_in_bytes;
+ uint8_t* last_pixel =
+ (pixel_data_begin + (number_pixels - 1) * pixel_offset_in_bytes);
+ for (uint8_t* current_pixel = second_pixel; current_pixel != last_pixel;
+ current_pixel += pixel_offset_in_bytes) {
+ current_color += color_step;
+ ColorStopToPixelData(pixel_format, current_color, current_pixel,
+ pixel_data_end);
+ }
+
+ ColorStopToPixelData(pixel_format, end_color, last_pixel, pixel_data_end);
+
+ return true;
+}
+
+void RenderOptimizedLinearGradient(SbBlitterDevice device,
+ SbBlitterContext context,
+ const RenderState& render_state,
+ cobalt::math::RectF rect,
+ const LinearGradientBrush& brush) {
+ if ((rect.width() == 0) && (rect.height() == 0)) return;
+
+ DCHECK(brush.IsVertical() || brush.IsHorizontal());
+
+ // The main strategy here is to create a 1D image, and then calculate
+ // the gradient values in software. If the gradient is simple, this can be
+ // one with optimized function (RenderSimpleGradient), which avoids calling
+ // Skia (and thus is faster). Otherwise, we call RenderComplexLinearGradient,
+ // which uses Skia.
+
+ // One a gradient is created, a SbBlitterSurface is created and a rectangle
+ // is blitted using the blitter API.
+ int width = brush.IsHorizontal() ? rect.width() : 1;
+ int height = brush.IsVertical() ? rect.height() : 1;
+
+ SkImageInfo image_info = SkImageInfo::MakeN32Premul(width, height);
+
+ SbBlitterPixelDataFormat pixel_format =
+ SkiaToBlitterPixelFormat(image_info.colorType());
+
+ if (!SbBlitterIsPixelFormatSupportedByPixelData(device, pixel_format)) {
+ NOTREACHED() << "Pixel Format is not supported by Pixel Data";
+ return;
+ }
+
+ SbBlitterPixelData pixel_data =
+ SbBlitterCreatePixelData(device, width, height, pixel_format);
+
+ int bytes_per_pixel = GetPixelOffsetInBytes(image_info, pixel_data);
+ int pitch_in_bytes = SbBlitterGetPixelDataPitchInBytes(pixel_data);
+
+ uint8_t* pixel_data_begin =
+ static_cast<uint8_t*>(SbBlitterGetPixelDataPointer(pixel_data));
+ uint8_t* pixel_data_end = pixel_data_begin + pitch_in_bytes * height;
+
+ if (pixel_data) {
+ const ColorStopList& color_stops(brush.color_stops());
+
+ int pixel_offset_in_bytes = (width == 1) ? pitch_in_bytes : bytes_per_pixel;
+ bool render_successful = RenderSimpleGradient(
+ brush, color_stops, width, height, pixel_format, pixel_data_begin,
+ pixel_data_end, pixel_offset_in_bytes);
+ if (!render_successful) {
+ RenderComplexLinearGradient(brush, color_stops, width, height, image_info,
+ pixel_data_begin, pitch_in_bytes);
+ }
+
+ SbBlitterSurface surface =
+ SbBlitterCreateSurfaceFromPixelData(device, pixel_data);
+
+ if (surface) {
+ SbBlitterSetBlending(context, true);
+ SbBlitterSetModulateBlitsWithColor(context, false);
+ cobalt::math::Rect transformed_rect =
+ RectFToRect(render_state.transform.TransformRect(rect));
+ SbBlitterRect source_rect = SbBlitterMakeRect(0, 0, width, height);
+ SbBlitterRect dest_rect = SbBlitterMakeRect(
+ transformed_rect.x(), transformed_rect.y(), transformed_rect.width(),
+ transformed_rect.height());
+ SbBlitterBlitRectToRect(context, surface, source_rect, dest_rect);
+
+ SbBlitterDestroySurface(surface);
+ }
+ }
+}
+} // namespace
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace blitter {
+
+using render_tree::LinearGradientBrush;
+
+bool RenderLinearGradient(SbBlitterDevice device, SbBlitterContext context,
+ const RenderState& render_state,
+ const render_tree::RectNode& rect_node) {
+ DCHECK(rect_node.data().background_brush);
+ const LinearGradientBrush* const linear_gradient_brush =
+ base::polymorphic_downcast<LinearGradientBrush*>(
+ rect_node.data().background_brush.get());
+ if (!linear_gradient_brush) return false;
+
+ // Currently, only vertical and horizontal gradients are accelerated.
+ if ((linear_gradient_brush->IsVertical() ||
+ linear_gradient_brush->IsHorizontal()) == false)
+ return false;
+
+ RenderOptimizedLinearGradient(device, context, render_state,
+ rect_node.data().rect, *linear_gradient_brush);
+ return true;
+}
+
+} // namespace blitter
+} // namespace rasterizer
+} // namespace renderer
+} // namespace cobalt
+
+#endif // #if SB_HAS(BLITTER)
diff --git a/src/cobalt/renderer/rasterizer/blitter/linear_gradient.h b/src/cobalt/renderer/rasterizer/blitter/linear_gradient.h
new file mode 100644
index 0000000..8340722
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/blitter/linear_gradient.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_RENDERER_RASTERIZER_BLITTER_LINEAR_GRADIENT_H_
+#define COBALT_RENDERER_RASTERIZER_BLITTER_LINEAR_GRADIENT_H_
+
+#include "cobalt/math/rect_f.h"
+#include "cobalt/render_tree/rect_node.h"
+#include "cobalt/renderer/rasterizer/blitter/render_state.h"
+#include "starboard/blitter.h"
+
+#ifndef SB_HAS_BLITTER
+#define SB_HAS_BLITTER
+#endif
+#if SB_HAS(BLITTER)
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace blitter {
+
+bool RenderLinearGradient(SbBlitterDevice device, SbBlitterContext context,
+ const RenderState& render_state,
+ const render_tree::RectNode& rect_node);
+
+} // namespace blitter
+} // namespace rasterizer
+} // namespace renderer
+} // namespace cobalt
+
+#endif // #if SB_HAS(BLITTER)
+
+#endif // COBALT_RENDERER_RASTERIZER_BLITTER_LINEAR_GRADIENT_H_
diff --git a/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp b/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp
index 07f6947..b5e0bec 100644
--- a/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp
+++ b/src/cobalt/renderer/rasterizer/blitter/rasterizer.gyp
@@ -42,6 +42,7 @@
'cobalt_blitter_conversions.cc',
'hardware_rasterizer.cc',
'image.cc',
+ 'linear_gradient.cc',
'render_state.cc',
'render_tree_blitter_conversions.cc',
'render_tree_node_visitor.cc',
@@ -81,4 +82,4 @@
],
},
],
-}
\ No newline at end of file
+}
diff --git a/src/cobalt/renderer/rasterizer/blitter/render_state.h b/src/cobalt/renderer/rasterizer/blitter/render_state.h
index 8b05887..b0a828a 100644
--- a/src/cobalt/renderer/rasterizer/blitter/render_state.h
+++ b/src/cobalt/renderer/rasterizer/blitter/render_state.h
@@ -25,6 +25,7 @@
#include "cobalt/math/rect.h"
#include "cobalt/math/rect_f.h"
#include "cobalt/math/vector2d_f.h"
+#include "cobalt/render_tree/brush.h"
#include "starboard/blitter.h"
#if SB_HAS(BLITTER)
diff --git a/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc
index 8726b2e..8fc3821 100644
--- a/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.cc
@@ -17,6 +17,7 @@
#include "cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.h"
#include "base/bind.h"
+#include "cobalt/base/polymorphic_downcast.h"
#include "cobalt/math/matrix3_f.h"
#include "cobalt/math/rect.h"
#include "cobalt/math/rect_f.h"
@@ -25,6 +26,7 @@
#include "cobalt/math/vector2d_f.h"
#include "cobalt/renderer/rasterizer/blitter/cobalt_blitter_conversions.h"
#include "cobalt/renderer/rasterizer/blitter/image.h"
+#include "cobalt/renderer/rasterizer/blitter/linear_gradient.h"
#include "cobalt/renderer/rasterizer/blitter/skia_blitter_conversions.h"
#include "cobalt/renderer/rasterizer/common/offscreen_render_coordinate_mapping.h"
#include "starboard/blitter.h"
@@ -44,6 +46,9 @@
using render_tree::Border;
using render_tree::Brush;
using render_tree::ColorRGBA;
+using render_tree::ColorStop;
+using render_tree::ColorStopList;
+using render_tree::LinearGradientBrush;
using render_tree::SolidColorBrush;
using render_tree::ViewportFilter;
@@ -364,34 +369,43 @@
return;
}
}
- if (rect_node->data().background_brush) {
- if (rect_node->data().background_brush->GetTypeId() !=
- base::GetTypeId<SolidColorBrush>()) {
- // We can only render solid color rectangles, if we have a more
- // complicated brush (like gradients), fallback to software.
- RenderWithSoftwareRenderer(rect_node);
- return;
- }
- }
const RectF& transformed_rect =
render_state_.transform.TransformRect(rect_node->data().rect);
- // Render the solid color fill, if a brush exists.
if (rect_node->data().background_brush) {
- SolidColorBrush* solid_color_brush =
- base::polymorphic_downcast<SolidColorBrush*>(
- rect_node->data().background_brush.get());
- ColorRGBA color = solid_color_brush->color();
+ base::TypeId background_brush_typeid(
+ rect_node->data().background_brush->GetTypeId());
- if (render_state_.opacity < 1.0f) {
- color.set_a(color.a() * render_state_.opacity);
+ if (background_brush_typeid == base::GetTypeId<SolidColorBrush>()) {
+ // Render the solid color fill, if a brush exists.
+ SolidColorBrush* solid_color_brush =
+ base::polymorphic_downcast<SolidColorBrush*>(
+ rect_node->data().background_brush.get());
+ ColorRGBA color = solid_color_brush->color();
+
+ if (render_state_.opacity < 1.0f) {
+ color.set_a(color.a() * render_state_.opacity);
+ }
+
+ SbBlitterSetBlending(context_, color.a() < 1.0f);
+ SbBlitterSetColor(context_, RenderTreeToBlitterColor(color));
+
+ SbBlitterFillRect(context_, RectFToBlitterRect(transformed_rect));
+ } else if (background_brush_typeid ==
+ base::GetTypeId<LinearGradientBrush>()) {
+ DCHECK(rect_node != NULL);
+ bool rendered_gradient =
+ RenderLinearGradient(device_, context_, render_state_, *rect_node);
+ if (!rendered_gradient) {
+ RenderWithSoftwareRenderer(rect_node);
+ return;
+ }
+ } else {
+ // If we have a more complicated brush fallback to software.
+ RenderWithSoftwareRenderer(rect_node);
+ return;
}
-
- SbBlitterSetBlending(context_, color.a() < 1.0f);
- SbBlitterSetColor(context_, RenderTreeToBlitterColor(color));
-
- SbBlitterFillRect(context_, RectFToBlitterRect(transformed_rect));
}
// Render the border, if it exists.
diff --git a/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.h b/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.h
index 4d9e0ee..938a5cf 100644
--- a/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.h
+++ b/src/cobalt/renderer/rasterizer/blitter/render_tree_node_visitor.h
@@ -39,6 +39,11 @@
#include "cobalt/renderer/rasterizer/common/surface_cache.h"
#include "starboard/blitter.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+
+#ifndef SB_HAS_BLITTER
+#define SB_HAS_BLITTER
+#endif
#if SB_HAS(BLITTER)
diff --git a/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.cc b/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.cc
index cad2346..6622fbc 100644
--- a/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.cc
@@ -39,7 +39,8 @@
void SoftwareRasterizer::Submit(
const scoped_refptr<render_tree::Node>& render_tree,
- const scoped_refptr<backend::RenderTarget>& render_target, int options) {
+ const scoped_refptr<backend::RenderTarget>& render_target,
+ const Options& options) {
int width = render_target->GetSize().width();
int height = render_target->GetSize().height();
diff --git a/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.h b/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.h
index 4743332..646f3c4 100644
--- a/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/blitter/software_rasterizer.h
@@ -44,7 +44,7 @@
void Submit(const scoped_refptr<render_tree::Node>& render_tree,
const scoped_refptr<backend::RenderTarget>& render_target,
- int options) OVERRIDE;
+ const Options& options) OVERRIDE;
render_tree::ResourceProvider* GetResourceProvider() OVERRIDE;
diff --git a/src/cobalt/renderer/rasterizer/egl/software_rasterizer.cc b/src/cobalt/renderer/rasterizer/egl/software_rasterizer.cc
index e778d0f..97fcc63 100644
--- a/src/cobalt/renderer/rasterizer/egl/software_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/egl/software_rasterizer.cc
@@ -40,7 +40,8 @@
void SoftwareRasterizer::Submit(
const scoped_refptr<render_tree::Node>& render_tree,
- const scoped_refptr<backend::RenderTarget>& render_target, int options) {
+ const scoped_refptr<backend::RenderTarget>& render_target,
+ const Options& options) {
int width = render_target->GetSize().width();
int height = render_target->GetSize().height();
diff --git a/src/cobalt/renderer/rasterizer/egl/software_rasterizer.h b/src/cobalt/renderer/rasterizer/egl/software_rasterizer.h
index 662353c..8ae5e84 100644
--- a/src/cobalt/renderer/rasterizer/egl/software_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/egl/software_rasterizer.h
@@ -44,7 +44,7 @@
void Submit(const scoped_refptr<render_tree::Node>& render_tree,
const scoped_refptr<backend::RenderTarget>& render_target,
- int options) OVERRIDE;
+ const Options& options) OVERRIDE;
render_tree::ResourceProvider* GetResourceProvider() OVERRIDE;
diff --git a/src/cobalt/renderer/rasterizer/rasterizer.h b/src/cobalt/renderer/rasterizer/rasterizer.h
index b15d4bb..fea7fe2 100644
--- a/src/cobalt/renderer/rasterizer/rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/rasterizer.h
@@ -18,6 +18,8 @@
#define COBALT_RENDERER_RASTERIZER_RASTERIZER_H_
#include "base/memory/ref_counted.h"
+#include "base/optional.h"
+#include "cobalt/math/rect.h"
#include "cobalt/render_tree/font.h"
#include "cobalt/render_tree/image.h"
#include "cobalt/render_tree/node.h"
@@ -45,7 +47,21 @@
public:
// When set, will clear the render target before rasterizing the render tree
// to it.
- static const int kSubmitOptions_Clear = (1 << 0);
+ static const int kSubmitFlags_Clear = (1 << 0);
+
+ struct Options {
+ Options() : flags(0) {}
+
+ // A bitwise combination of any of the |kSubmitFlags_*| constants defined
+ // above.
+ int flags;
+
+ // If specified, indicates which region of |render_target| is
+ // dirty and needs to be updated. If animations are playing for example,
+ // then |dirty| can be setup to bound the animations. A rasterizer is free
+ // to ignore this value if they wish.
+ base::optional<math::Rect> dirty;
+ };
virtual ~Rasterizer() {}
@@ -54,11 +70,11 @@
// above.
virtual void Submit(const scoped_refptr<render_tree::Node>& render_tree,
const scoped_refptr<backend::RenderTarget>& render_target,
- int options) = 0;
+ const Options& options) = 0;
void Submit(const scoped_refptr<render_tree::Node>& render_tree,
const scoped_refptr<backend::RenderTarget>& render_target) {
- Submit(render_tree, render_target, 0);
+ Submit(render_tree, render_target, Options());
}
// Returns a thread-safe object from which one can produce renderer resources
diff --git a/src/cobalt/renderer/rasterizer/skia/conversions.h b/src/cobalt/renderer/rasterizer/skia/conversions.h
new file mode 100644
index 0000000..c5640e4
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/skia/conversions.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_RENDERER_RASTERIZER_SKIA_CONVERSIONS_H_
+#define COBALT_RENDERER_RASTERIZER_SKIA_CONVERSIONS_H_
+
+#include <vector>
+#include "third_party/skia/include/core/SkColor.h"
+
+namespace cobalt {
+namespace renderer {
+namespace rasterizer {
+namespace skia {
+
+inline SkColor ToSkColor(const render_tree::ColorRGBA& color) {
+ return SkColorSetARGB(color.rgb8_a(), color.rgb8_r(), color.rgb8_g(),
+ color.rgb8_b());
+}
+
+// Helper struct to convert render_tree::ColorStopList to a format that Skia
+// methods will easily accept.
+struct SkiaColorStops {
+ explicit SkiaColorStops(const render_tree::ColorStopList& color_stops)
+ : colors(color_stops.size()),
+ positions(color_stops.size()),
+ has_alpha(false) {
+ for (size_t i = 0; i < color_stops.size(); ++i) {
+ if (color_stops[i].color.a() < 1.0f) {
+ has_alpha = true;
+ }
+
+ colors[i] = ToSkColor(color_stops[i].color);
+ positions[i] = color_stops[i].position;
+ }
+ }
+
+ std::size_t size() const {
+ DCHECK(colors.size() == positions.size());
+ return colors.size();
+ }
+
+ std::vector<SkColor> colors;
+ std::vector<SkScalar> positions;
+ bool has_alpha;
+};
+
+} // namespace skia
+} // namespace rasterizer
+} // namespace renderer
+} // namespace cobalt
+
+#endif // COBALT_RENDERER_RASTERIZER_SKIA_CONVERSIONS_H_
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
index 1c55dc0..c3e2a0d 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.cc
@@ -47,7 +47,7 @@
void Submit(const scoped_refptr<render_tree::Node>& render_tree,
const scoped_refptr<backend::RenderTarget>& render_target,
- int options);
+ const Options& options);
render_tree::ResourceProvider* GetResourceProvider();
@@ -191,7 +191,8 @@
void HardwareRasterizer::Impl::Submit(
const scoped_refptr<render_tree::Node>& render_tree,
- const scoped_refptr<backend::RenderTarget>& render_target, int options) {
+ const scoped_refptr<backend::RenderTarget>& render_target,
+ const Options& options) {
DCHECK(thread_checker_.CalledOnValidThread());
backend::RenderTargetEGL* render_target_egl =
@@ -226,8 +227,9 @@
// Get a SkCanvas that outputs to our hardware render target.
SkCanvas* canvas = sk_output_surface_->getCanvas();
+ canvas->save();
- if (options & Rasterizer::kSubmitOptions_Clear) {
+ if (options.flags & Rasterizer::kSubmitFlags_Clear) {
canvas->clear(SkColorSetARGB(0, 0, 0, 0));
}
@@ -253,6 +255,7 @@
}
graphics_context_->SwapBuffers(render_target_egl);
+ canvas->restore();
}
render_tree::ResourceProvider* HardwareRasterizer::Impl::GetResourceProvider() {
@@ -321,7 +324,8 @@
void HardwareRasterizer::Submit(
const scoped_refptr<render_tree::Node>& render_tree,
- const scoped_refptr<backend::RenderTarget>& render_target, int options) {
+ const scoped_refptr<backend::RenderTarget>& render_target,
+ const Options& options) {
TRACE_EVENT0("cobalt::renderer", "Rasterizer::Submit()");
TRACE_EVENT0("cobalt::renderer", "HardwareRasterizer::Submit()");
impl_->Submit(render_tree, render_target, options);
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.h b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.h
index 8807fc3..af2b78e 100644
--- a/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/skia/hardware_rasterizer.h
@@ -59,7 +59,7 @@
// into the constructor.
void Submit(const scoped_refptr<render_tree::Node>& render_tree,
const scoped_refptr<backend::RenderTarget>& render_target,
- int options) OVERRIDE;
+ const Options& options) OVERRIDE;
render_tree::ResourceProvider* GetResourceProvider() OVERRIDE;
diff --git a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.cc b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.cc
index 964fad6..5ab5536 100644
--- a/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.cc
+++ b/src/cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.cc
@@ -16,7 +16,11 @@
#include "cobalt/renderer/rasterizer/skia/skia/src/ports/SkFontMgr_cobalt.h"
+#if defined(STARBOARD)
+#include "starboard/file.h"
+#else
#include <sys/stat.h>
+#endif
#include <cmath>
#include "base/at_exit.h"
@@ -247,8 +251,13 @@
SkString path_name(SkOSPath::Join(base_path, font_file.file_name.c_str()));
// Sanity check that something exists at this location.
+#if defined(STARBOARD)
+ bool is_font_file_found = SbFileExists(path_name.c_str());
+#else
struct stat status;
- if (0 != stat(path_name.c_str(), &status)) {
+ bool is_font_file_found = (stat(path_name.c_str(), &status) == 0);
+#endif // defined(STARBOARD)
+ if (!is_font_file_found) {
LOG(ERROR) << "Failed to find font file: " << path_name.c_str();
continue;
}
diff --git a/src/cobalt/renderer/rasterizer/stub/rasterizer.h b/src/cobalt/renderer/rasterizer/stub/rasterizer.h
index 2c4f5bf..ff69528 100644
--- a/src/cobalt/renderer/rasterizer/stub/rasterizer.h
+++ b/src/cobalt/renderer/rasterizer/stub/rasterizer.h
@@ -31,7 +31,7 @@
void Submit(const scoped_refptr<render_tree::Node>& render_tree,
const scoped_refptr<backend::RenderTarget>& render_target,
- int options) OVERRIDE {}
+ const Options& options) OVERRIDE {}
render_tree::ResourceProvider* GetResourceProvider() OVERRIDE;
diff --git a/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatNone-expected.png b/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatNone-expected.png
deleted file mode 100644
index b8638eb..0000000
--- a/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatNone-expected.png
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatNoneAndRoundedCorners-expected.png b/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatNoneAndRoundedCorners-expected.png
deleted file mode 100644
index 2076087..0000000
--- a/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatNoneAndRoundedCorners-expected.png
+++ /dev/null
Binary files differ
diff --git a/src/cobalt/renderer/render_tree_pixel_tester.cc b/src/cobalt/renderer/render_tree_pixel_tester.cc
index 6e6fa75..717ab06 100644
--- a/src/cobalt/renderer/render_tree_pixel_tester.cc
+++ b/src/cobalt/renderer/render_tree_pixel_tester.cc
@@ -311,8 +311,9 @@
scoped_array<uint8_t> RenderTreePixelTester::RasterizeRenderTree(
const scoped_refptr<cobalt::render_tree::Node>& tree) const {
// Rasterize the test render tree to the rasterizer's offscreen render target.
- rasterizer_->Submit(tree, test_surface_,
- rasterizer::Rasterizer::kSubmitOptions_Clear);
+ rasterizer::Rasterizer::Options rasterizer_options;
+ rasterizer_options.flags = rasterizer::Rasterizer::kSubmitFlags_Clear;
+ rasterizer_->Submit(tree, test_surface_, rasterizer_options);
// Load the texture's pixel data into a CPU memory buffer and return it.
return graphics_context_->DownloadPixelDataAsRGBA(test_surface_);
diff --git a/src/cobalt/script/mozjs/mozjs_engine.cc b/src/cobalt/script/mozjs/mozjs_engine.cc
index 0acb744..623a7e5 100644
--- a/src/cobalt/script/mozjs/mozjs_engine.cc
+++ b/src/cobalt/script/mozjs/mozjs_engine.cc
@@ -20,7 +20,7 @@
#include "base/logging.h"
#include "cobalt/base/c_val.h"
-#include "cobalt/browser/web_module.h"
+#include "cobalt/browser/stack_size_constants.h"
#include "cobalt/script/mozjs/mozjs_global_environment.h"
#include "third_party/mozjs/cobalt_config/include/jscustomallocator.h"
#include "third_party/mozjs/js/src/jsapi.h"
@@ -111,7 +111,7 @@
// Setting three quarters of the web module stack size to ensure that native
// stack won't exceed the stack size.
JS_SetNativeStackQuota(runtime_,
- browser::WebModule::kWebModuleStackSize / 4 * 3);
+ cobalt::browser::kWebModuleStackSize / 4 * 3);
JS_SetRuntimePrivate(runtime_, this);
diff --git a/src/cobalt/script/mozjs/mozjs_exception_state.cc b/src/cobalt/script/mozjs/mozjs_exception_state.cc
index 9b2a5ec..39a611a 100644
--- a/src/cobalt/script/mozjs/mozjs_exception_state.cc
+++ b/src/cobalt/script/mozjs/mozjs_exception_state.cc
@@ -44,6 +44,8 @@
case kURIError:
return JSEXN_URIERR;
}
+ NOTREACHED();
+ return JSEXN_ERR;
}
// JSErrorCallback.
diff --git a/src/cobalt/speech/chunked_byte_buffer.cc b/src/cobalt/speech/chunked_byte_buffer.cc
deleted file mode 100644
index 43772df..0000000
--- a/src/cobalt/speech/chunked_byte_buffer.cc
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "cobalt/speech/chunked_byte_buffer.h"
-
-#include <algorithm>
-#include <utility>
-
-#include "base/basictypes.h"
-#include "base/logging.h"
-
-namespace {
-
-static const size_t kHeaderLength = sizeof(uint32_t);
-
-COMPILE_ASSERT(sizeof(size_t) >= kHeaderLength,
- chunked_byte_buffer_not_supported_on_this_architecture);
-
-uint32_t ReadBigEndian32(const uint8_t* buffer) {
- return (static_cast<uint32_t>(buffer[3])) |
- (static_cast<uint32_t>(buffer[2]) << 8) |
- (static_cast<uint32_t>(buffer[1]) << 16) |
- (static_cast<uint32_t>(buffer[0]) << 24);
-}
-
-} // namespace
-
-namespace cobalt {
-namespace speech {
-
-ChunkedByteBuffer::ChunkedByteBuffer()
- : partial_chunk_(new Chunk()),
- total_bytes_stored_(0) {
-}
-
-ChunkedByteBuffer::~ChunkedByteBuffer() {
- Clear();
-}
-
-void ChunkedByteBuffer::Append(const uint8_t* start, size_t length) {
- size_t remaining_bytes = length;
- const uint8_t* next_data = start;
-
- while (remaining_bytes > 0) {
- DCHECK(partial_chunk_ != NULL);
- size_t insert_length = 0;
- bool header_completed = false;
- bool content_completed = false;
- std::vector<uint8_t>* insert_target;
-
- if (partial_chunk_->header.size() < kHeaderLength) {
- const size_t bytes_to_complete_header =
- kHeaderLength - partial_chunk_->header.size();
- insert_length = std::min(bytes_to_complete_header, remaining_bytes);
- insert_target = &partial_chunk_->header;
- header_completed = (remaining_bytes >= bytes_to_complete_header);
- } else {
- DCHECK_LT(partial_chunk_->content->size(),
- partial_chunk_->ExpectedContentLength());
- const size_t bytes_to_complete_chunk =
- partial_chunk_->ExpectedContentLength() -
- partial_chunk_->content->size();
- insert_length = std::min(bytes_to_complete_chunk, remaining_bytes);
- insert_target = partial_chunk_->content.get();
- content_completed = (remaining_bytes >= bytes_to_complete_chunk);
- }
-
- DCHECK_GT(insert_length, 0U);
- DCHECK_LE(insert_length, remaining_bytes);
- DCHECK_LE(next_data + insert_length, start + length);
- insert_target->insert(insert_target->end(),
- next_data,
- next_data + insert_length);
- next_data += insert_length;
- remaining_bytes -= insert_length;
-
- if (header_completed) {
- DCHECK_EQ(partial_chunk_->header.size(), kHeaderLength);
- if (partial_chunk_->ExpectedContentLength() == 0) {
- // Handle zero-byte chunks.
- chunks_.push_back(partial_chunk_.release());
- partial_chunk_.reset(new Chunk());
- } else {
- partial_chunk_->content->reserve(
- partial_chunk_->ExpectedContentLength());
- }
- } else if (content_completed) {
- DCHECK_EQ(partial_chunk_->content->size(),
- partial_chunk_->ExpectedContentLength());
- chunks_.push_back(partial_chunk_.release());
- partial_chunk_.reset(new Chunk());
- }
- }
- DCHECK_EQ(next_data, start + length);
- total_bytes_stored_ += length;
-}
-
-void ChunkedByteBuffer::Append(const std::string& string) {
- Append(reinterpret_cast<const uint8_t*>(string.data()), string.size());
-}
-
-bool ChunkedByteBuffer::HasChunks() const {
- return !chunks_.empty();
-}
-
-scoped_ptr<std::vector<uint8_t> > ChunkedByteBuffer::PopChunk() {
- if (chunks_.empty())
- return scoped_ptr<std::vector<uint8_t> >().Pass();
- scoped_ptr<Chunk> chunk(*chunks_.begin());
- chunks_.weak_erase(chunks_.begin());
- DCHECK_EQ(chunk->header.size(), kHeaderLength);
- DCHECK_EQ(chunk->content->size(), chunk->ExpectedContentLength());
- total_bytes_stored_ -= chunk->content->size();
- total_bytes_stored_ -= kHeaderLength;
- return chunk->content.Pass();
-}
-
-void ChunkedByteBuffer::Clear() {
- chunks_.clear();
- partial_chunk_.reset(new Chunk());
- total_bytes_stored_ = 0;
-}
-
-ChunkedByteBuffer::Chunk::Chunk() : content(new std::vector<uint8_t>()) {}
-
-ChunkedByteBuffer::Chunk::~Chunk() {
-}
-
-size_t ChunkedByteBuffer::Chunk::ExpectedContentLength() const {
- DCHECK_EQ(header.size(), kHeaderLength);
- return static_cast<size_t>(ReadBigEndian32(&header[0]));
-}
-
-} // namespace speech
-} // namespace cobalt
diff --git a/src/cobalt/speech/chunked_byte_buffer.h b/src/cobalt/speech/chunked_byte_buffer.h
deleted file mode 100644
index f69dec9..0000000
--- a/src/cobalt/speech/chunked_byte_buffer.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COBALT_SPEECH_CHUNKED_BYTE_BUFFER_H_
-#define COBALT_SPEECH_CHUNKED_BYTE_BUFFER_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/scoped_vector.h"
-
-namespace cobalt {
-namespace speech {
-
-// Models a chunk-oriented byte buffer. The term chunk is herein defined as an
-// arbitrary sequence of bytes that is preceeded by N header bytes, indicating
-// its size. Data may be appended to the buffer with no particular respect of
-// chunks boundaries. However, chunks can be extracted (FIFO) only when their
-// content (according to their header) is fully available in the buffer.
-// The current implementation support only 4 byte Big Endian headers.
-// Empty chunks (i.e. the sequence 00 00 00 00) are NOT allowed.
-//
-// E.g. 00 00 00 04 xx xx xx xx 00 00 00 02 yy yy 00 00 00 04 zz zz zz zz
-// [----- CHUNK 1 -------] [--- CHUNK 2 ---] [------ CHUNK 3 ------]
-class ChunkedByteBuffer {
- public:
- ChunkedByteBuffer();
- ~ChunkedByteBuffer();
-
- // Appends |length| bytes starting from |start| to the buffer.
- void Append(const uint8_t* start, size_t length);
-
- // Appends bytes contained in the |string| to the buffer.
- void Append(const std::string& string);
-
- // Checks whether one or more complete chunks are available in the buffer.
- bool HasChunks() const;
-
- // If enough data is available, reads and removes the first complete chunk
- // from the buffer. Returns a NULL pointer if no complete chunk is available.
- scoped_ptr<std::vector<uint8_t> > PopChunk();
-
- // Clears all the content of the buffer.
- void Clear();
-
- // Returns the number of raw bytes (including headers) present.
- size_t GetTotalLength() const { return total_bytes_stored_; }
-
- private:
- struct Chunk {
- Chunk();
- ~Chunk();
-
- std::vector<uint8_t> header;
- scoped_ptr<std::vector<uint8_t> > content;
- size_t ExpectedContentLength() const;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(Chunk);
- };
-
- ScopedVector<Chunk> chunks_;
- scoped_ptr<Chunk> partial_chunk_;
- size_t total_bytes_stored_;
-
- DISALLOW_COPY_AND_ASSIGN(ChunkedByteBuffer);
-};
-
-} // namespace speech
-} // namespace cobalt
-
-#endif // COBALT_SPEECH_CHUNKED_BYTE_BUFFER_H_
diff --git a/src/cobalt/speech/chunked_byte_buffer_unittest.cc b/src/cobalt/speech/chunked_byte_buffer_unittest.cc
deleted file mode 100644
index 0db0acd..0000000
--- a/src/cobalt/speech/chunked_byte_buffer_unittest.cc
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <string>
-#include <vector>
-
-#include "cobalt/speech/chunked_byte_buffer.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace cobalt {
-namespace speech {
-
-typedef std::vector<uint8_t> ByteVector;
-
-TEST(ChunkedByteBufferTest, BasicTest) {
- ChunkedByteBuffer buffer;
-
- const uint8_t kChunks[] = {
- 0x00, 0x00, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04, // Chunk 1: 4 bytes
- 0x00, 0x00, 0x00, 0x02, 0x05, 0x06, // Chunk 2: 2 bytes
- 0x00, 0x00, 0x00, 0x01, 0x07 // Chunk 3: 1 bytes
- };
-
- EXPECT_EQ(0U, buffer.GetTotalLength());
- EXPECT_FALSE(buffer.HasChunks());
-
- // Append partially chunk 1.
- buffer.Append(kChunks, 2);
- EXPECT_EQ(2U, buffer.GetTotalLength());
- EXPECT_FALSE(buffer.HasChunks());
-
- // Complete chunk 1.
- buffer.Append(kChunks + 2, 6);
- EXPECT_EQ(8U, buffer.GetTotalLength());
- EXPECT_TRUE(buffer.HasChunks());
-
- // Append fully chunk 2.
- buffer.Append(kChunks + 8, 6);
- EXPECT_EQ(14U, buffer.GetTotalLength());
- EXPECT_TRUE(buffer.HasChunks());
-
- // Remove and check chunk 1.
- scoped_ptr<ByteVector> chunk;
- chunk = buffer.PopChunk().Pass();
- EXPECT_TRUE(chunk != NULL);
- EXPECT_EQ(4U, chunk->size());
- EXPECT_EQ(0, std::char_traits<uint8_t>::compare(kChunks + 4, &(*chunk)[0],
- chunk->size()));
- EXPECT_EQ(6U, buffer.GetTotalLength());
- EXPECT_TRUE(buffer.HasChunks());
-
- // Read and check chunk 2.
- chunk = buffer.PopChunk().Pass();
- EXPECT_TRUE(chunk != NULL);
- EXPECT_EQ(2U, chunk->size());
- EXPECT_EQ(0, std::char_traits<uint8_t>::compare(kChunks + 12, &(*chunk)[0],
- chunk->size()));
- EXPECT_EQ(0U, buffer.GetTotalLength());
- EXPECT_FALSE(buffer.HasChunks());
-
- // Append fully chunk 3.
- buffer.Append(kChunks + 14, 5);
- EXPECT_EQ(5U, buffer.GetTotalLength());
-
- // Remove and check chunk 3.
- chunk = buffer.PopChunk().Pass();
- EXPECT_TRUE(chunk != NULL);
- EXPECT_EQ(1U, chunk->size());
- EXPECT_EQ((*chunk)[0], kChunks[18]);
- EXPECT_EQ(0U, buffer.GetTotalLength());
- EXPECT_FALSE(buffer.HasChunks());
-}
-
-} // namespace speech
-} // namespace cobalt
diff --git a/src/cobalt/speech/endpointer/endpointer.cc b/src/cobalt/speech/endpointer/endpointer.cc
deleted file mode 100644
index cabad1a..0000000
--- a/src/cobalt/speech/endpointer/endpointer.cc
+++ /dev/null
@@ -1,187 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "cobalt/speech/endpointer/endpointer.h"
-
-#include "base/time.h"
-
-using base::Time;
-
-namespace {
-// Only send |kFrameSize| of audio data to energy endpointer each time.
-// It should be smaller than the samples of audio bus which is passed in
-// |ProcessAudio|.
-const int kFrameSize = 160;
-}
-
-namespace cobalt {
-namespace speech {
-
-Endpointer::Endpointer(int sample_rate)
- : speech_input_possibly_complete_silence_length_us_(-1),
- speech_input_complete_silence_length_us_(-1),
- audio_frame_time_us_(0),
- sample_rate_(sample_rate),
- frame_rate_(sample_rate / kFrameSize) {
- Reset();
-
- speech_input_minimum_length_us_ =
- static_cast<int64_t>(1.7 * Time::kMicrosecondsPerSecond);
- speech_input_complete_silence_length_us_ =
- static_cast<int64_t>(0.5 * Time::kMicrosecondsPerSecond);
- long_speech_input_complete_silence_length_us_ = -1;
- long_speech_length_us_ = -1;
- speech_input_possibly_complete_silence_length_us_ =
- 1 * Time::kMicrosecondsPerSecond;
-
- // Set the default configuration for Push To Talk mode.
- EnergyEndpointerParams ep_config;
- ep_config.set_frame_period(1.0f / static_cast<float>(frame_rate_));
- ep_config.set_frame_duration(1.0f / static_cast<float>(frame_rate_));
- ep_config.set_endpoint_margin(0.2f);
- ep_config.set_onset_window(0.15f);
- ep_config.set_speech_on_window(0.4f);
- ep_config.set_offset_window(0.15f);
- ep_config.set_onset_detect_dur(0.09f);
- ep_config.set_onset_confirm_dur(0.075f);
- ep_config.set_on_maintain_dur(0.10f);
- ep_config.set_offset_confirm_dur(0.12f);
- ep_config.set_decision_threshold(1000.0f);
- ep_config.set_min_decision_threshold(50.0f);
- ep_config.set_fast_update_dur(0.2f);
- ep_config.set_sample_rate(static_cast<float>(sample_rate));
- ep_config.set_min_fundamental_frequency(57.143f);
- ep_config.set_max_fundamental_frequency(400.0f);
- ep_config.set_contamination_rejection_period(0.25f);
- energy_endpointer_.Init(ep_config);
-}
-
-void Endpointer::Reset() {
- old_ep_status_ = EP_PRE_SPEECH;
- waiting_for_speech_possibly_complete_timeout_ = false;
- waiting_for_speech_complete_timeout_ = false;
- speech_previously_detected_ = false;
- speech_input_complete_ = false;
- audio_frame_time_us_ = 0; // Reset time for packets sent to endpointer.
- speech_end_time_us_ = -1;
- speech_start_time_us_ = -1;
-}
-
-void Endpointer::StartSession() {
- Reset();
- energy_endpointer_.StartSession();
-}
-
-void Endpointer::EndSession() {
- energy_endpointer_.EndSession();
-}
-
-void Endpointer::SetEnvironmentEstimationMode() {
- Reset();
- energy_endpointer_.SetEnvironmentEstimationMode();
-}
-
-void Endpointer::SetUserInputMode() {
- energy_endpointer_.SetUserInputMode();
-}
-
-EpStatus Endpointer::Status(int64_t* time) {
- return energy_endpointer_.Status(time);
-}
-
-EpStatus Endpointer::ProcessAudio(
- const ShellAudioBus& audio_bus, float* rms_out) {
- DCHECK_EQ(audio_bus.channels(), 1);
-
- const size_t num_samples = audio_bus.frames();
- const int16_t* audio_data = NULL;
-
- ShellAudioBus int16_audio_bus(1, num_samples, ShellAudioBus::kInt16,
- ShellAudioBus::kInterleaved);
-
- if (audio_bus.sample_type() == ShellAudioBus::kFloat32) {
- int16_audio_bus.Assign(audio_bus);
- DCHECK_EQ(int16_audio_bus.sample_type(), ShellAudioBus::kInt16);
- audio_data =
- reinterpret_cast<const int16_t*>(int16_audio_bus.interleaved_data());
- } else {
- DCHECK_EQ(audio_bus.sample_type(), ShellAudioBus::kInt16);
- audio_data =
- reinterpret_cast<const int16_t*>(audio_bus.interleaved_data());
- }
-
- EpStatus ep_status = EP_PRE_SPEECH;
-
- // Process the input data in blocks of kFrameSize, dropping any incomplete
- // frames at the end (which is ok since typically the caller will be recording
- // audio in multiples of our frame size).
- int sample_index = 0;
- while (static_cast<size_t>(sample_index + kFrameSize) <= num_samples) {
- // Have the endpointer process the frame.
- energy_endpointer_.ProcessAudioFrame(
- audio_frame_time_us_, audio_data + sample_index, kFrameSize, rms_out);
- sample_index += kFrameSize;
- audio_frame_time_us_ +=
- (kFrameSize * Time::kMicrosecondsPerSecond) / sample_rate_;
-
- // Get the status of the endpointer.
- int64_t ep_time;
- ep_status = energy_endpointer_.Status(&ep_time);
-
- // Handle state changes.
- if ((EP_SPEECH_PRESENT == ep_status) &&
- (EP_POSSIBLE_ONSET == old_ep_status_)) {
- speech_end_time_us_ = -1;
- waiting_for_speech_possibly_complete_timeout_ = false;
- waiting_for_speech_complete_timeout_ = false;
- // Trigger SpeechInputDidStart event on first detection.
- if (false == speech_previously_detected_) {
- speech_previously_detected_ = true;
- speech_start_time_us_ = ep_time;
- }
- }
- if ((EP_PRE_SPEECH == ep_status) &&
- (EP_POSSIBLE_OFFSET == old_ep_status_)) {
- speech_end_time_us_ = ep_time;
- waiting_for_speech_possibly_complete_timeout_ = true;
- waiting_for_speech_complete_timeout_ = true;
- }
- if (ep_time > speech_input_minimum_length_us_) {
- // Speech possibly complete timeout.
- if ((waiting_for_speech_possibly_complete_timeout_) &&
- (ep_time - speech_end_time_us_ >
- speech_input_possibly_complete_silence_length_us_)) {
- waiting_for_speech_possibly_complete_timeout_ = false;
- }
- if (waiting_for_speech_complete_timeout_) {
- // The length of the silence timeout period can be held constant, or it
- // can be changed after a fixed amount of time from the beginning of
- // speech.
- bool has_stepped_silence =
- (long_speech_length_us_ > 0) &&
- (long_speech_input_complete_silence_length_us_ > 0);
- int64_t requested_silence_length;
- if (has_stepped_silence &&
- (ep_time - speech_start_time_us_) > long_speech_length_us_) {
- requested_silence_length =
- long_speech_input_complete_silence_length_us_;
- } else {
- requested_silence_length =
- speech_input_complete_silence_length_us_;
- }
-
- // Speech complete timeout.
- if ((ep_time - speech_end_time_us_) > requested_silence_length) {
- waiting_for_speech_complete_timeout_ = false;
- speech_input_complete_ = true;
- }
- }
- }
- old_ep_status_ = ep_status;
- }
- return ep_status;
-}
-
-} // namespace speech
-} // namespace cobalt
diff --git a/src/cobalt/speech/endpointer/endpointer.h b/src/cobalt/speech/endpointer/endpointer.h
deleted file mode 100644
index f6c818f..0000000
--- a/src/cobalt/speech/endpointer/endpointer.h
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COBALT_SPEECH_ENDPOINTER_ENDPOINTER_H_
-#define COBALT_SPEECH_ENDPOINTER_ENDPOINTER_H_
-
-#include <stdint.h>
-
-#include "cobalt/speech/endpointer/energy_endpointer.h"
-#include "media/base/shell_audio_bus.h"
-
-class EpStatus;
-
-namespace cobalt {
-namespace speech {
-
-// A simple interface to the underlying energy-endpointer implementation, this
-// class lets callers provide audio as being recorded and let them poll to find
-// when the user has stopped speaking.
-//
-// There are two events that may trigger the end of speech:
-//
-// speechInputPossiblyComplete event:
-//
-// Signals that silence/noise has been detected for a *short* amount of
-// time after some speech has been detected. It can be used for low latency
-// UI feedback. To disable it, set it to a large amount.
-//
-// speechInputComplete event:
-//
-// This event is intended to signal end of input and to stop recording.
-// The amount of time to wait after speech is set by
-// speech_input_complete_silence_length_ and optionally two other
-// parameters (see below).
-// This time can be held constant, or can change as more speech is detected.
-// In the latter case, the time changes after a set amount of time from the
-// *beginning* of speech. This is motivated by the expectation that there
-// will be two distinct types of inputs: short search queries and longer
-// dictation style input.
-//
-// Three parameters are used to define the piecewise constant timeout function.
-// The timeout length is speech_input_complete_silence_length until
-// long_speech_length, when it changes to
-// long_speech_input_complete_silence_length.
-class Endpointer {
- public:
- typedef ::media::ShellAudioBus ShellAudioBus;
-
- explicit Endpointer(int sample_rate);
-
- // Start the endpointer. This should be called at the beginning of a session.
- void StartSession();
-
- // Stop the endpointer.
- void EndSession();
-
- // Start environment estimation. Audio will be used for environment estimation
- // i.e. noise level estimation.
- void SetEnvironmentEstimationMode();
-
- // Start user input. This should be called when the user indicates start of
- // input, e.g. by pressing a button.
- void SetUserInputMode();
-
- // Process a segment of audio, which may be more than one frame.
- // The status of the last frame will be returned.
- EpStatus ProcessAudio(const ShellAudioBus& audio_bus, float* rms_out);
-
- // Get the status of the endpointer.
- EpStatus Status(int64_t* time_us);
-
- // Returns true if the endpointer detected reasonable audio levels above
- // background noise which could be user speech, false if not.
- bool DidStartReceivingSpeech() const {
- return speech_previously_detected_;
- }
-
- bool IsEstimatingEnvironment() const {
- return energy_endpointer_.estimating_environment();
- }
-
- void set_speech_input_complete_silence_length(int64_t time_us) {
- speech_input_complete_silence_length_us_ = time_us;
- }
-
- void set_long_speech_input_complete_silence_length(int64_t time_us) {
- long_speech_input_complete_silence_length_us_ = time_us;
- }
-
- void set_speech_input_possibly_complete_silence_length(int64_t time_us) {
- speech_input_possibly_complete_silence_length_us_ = time_us;
- }
-
- void set_long_speech_length(int64_t time_us) {
- long_speech_length_us_ = time_us;
- }
-
- bool speech_input_complete() const {
- return speech_input_complete_;
- }
-
- int sample_rate() const { return sample_rate_; }
-
- // RMS background noise level in dB.
- float NoiseLevelDb() const { return energy_endpointer_.GetNoiseLevelDb(); }
-
- private:
- // Reset internal states. Helper method common to initial input utterance
- // and following input utternaces.
- void Reset();
-
- // Minimum allowable length of speech input.
- int64_t speech_input_minimum_length_us_;
-
- // The speechInputPossiblyComplete event signals that silence/noise has been
- // detected for a *short* amount of time after some speech has been detected.
- // This proporty specifies the time period.
- int64_t speech_input_possibly_complete_silence_length_us_;
-
- // The speechInputComplete event signals that silence/noise has been
- // detected for a *long* amount of time after some speech has been detected.
- // This property specifies the time period.
- int64_t speech_input_complete_silence_length_us_;
-
- // Same as above, this specifies the required silence period after speech
- // detection. This period is used instead of
- // speech_input_complete_silence_length_ when the utterance is longer than
- // long_speech_length_. This parameter is optional.
- int64_t long_speech_input_complete_silence_length_us_;
-
- // The period of time after which the endpointer should consider
- // long_speech_input_complete_silence_length_ as a valid silence period
- // instead of speech_input_complete_silence_length_. This parameter is
- // optional.
- int64_t long_speech_length_us_;
-
- // First speech onset time, used in determination of speech complete timeout.
- int64_t speech_start_time_us_;
-
- // Most recent end time, used in determination of speech complete timeout.
- int64_t speech_end_time_us_;
-
- int64_t audio_frame_time_us_;
- EpStatus old_ep_status_;
- bool waiting_for_speech_possibly_complete_timeout_;
- bool waiting_for_speech_complete_timeout_;
- bool speech_previously_detected_;
- bool speech_input_complete_;
- EnergyEndpointer energy_endpointer_;
- int sample_rate_;
- // 1 frame = (1 / frame_rate_) second of audio.
- int frame_rate_;
-};
-
-} // namespace speech
-} // namespace cobalt
-
-#endif // COBALT_SPEECH_ENDPOINTER_ENDPOINTER_H_
diff --git a/src/cobalt/speech/endpointer/endpointer_unittest.cc b/src/cobalt/speech/endpointer/endpointer_unittest.cc
deleted file mode 100644
index 29465d5..0000000
--- a/src/cobalt/speech/endpointer/endpointer_unittest.cc
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include <stdint.h>
-
-#include "cobalt/speech/endpointer/endpointer.h"
-#include "media/base/shell_audio_bus.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-const int kFrameRate = 50; // 20 ms long frames for AMR encoding.
-const int kSampleRate = 8000; // 8 k samples per second for AMR encoding.
-
-// At 8 sample per second a 20 ms frame is 160 samples, which corrsponds
-// to the AMR codec.
-const int kFrameSize = kSampleRate / kFrameRate; // 160 samples.
-
-#if defined(OS_STARBOARD)
-SB_COMPILE_ASSERT(kFrameSize == 160, invalid_frame_size);
-#else
-COMPILE_ASSERT(kFrameSize == 160, invalid_frame_size);
-#endif // defined(OS_STARBOARD)
-} // namespace
-
-namespace cobalt {
-namespace speech {
-
-class FrameProcessor {
- public:
- // Process a single frame of test audio samples.
- virtual EpStatus ProcessFrame(int64_t time,
- int16_t* samples,
- int frame_size) = 0;
-};
-
-void RunEndpointerEventsTest(FrameProcessor* processor) {
- int16_t samples[kFrameSize];
-
- // We will create a white noise signal of 150 frames. The frames from 50 to
- // 100 will have more power, and the endpointer should fire on those frames.
- const int kNumFrames = 150;
-
- // Create a random sequence of samples.
- srand(1);
- float gain = 0.0;
- int64_t time = 0;
- for (int frame_count = 0; frame_count < kNumFrames; ++frame_count) {
- // The frames from 50 to 100 will have more power, and the endpointer
- // should detect those frames as speech.
- if ((frame_count >= 50) && (frame_count < 100)) {
- gain = 2000.0;
- } else {
- gain = 1.0;
- }
- // Create random samples.
- for (int i = 0; i < kFrameSize; ++i) {
- float randNum = static_cast<float>(rand() - (RAND_MAX / 2)) /
- static_cast<float>(RAND_MAX);
- samples[i] = static_cast<int16_t>(gain * randNum);
- }
-
- EpStatus ep_status = processor->ProcessFrame(time, samples, kFrameSize);
- time += static_cast<int64_t>(kFrameSize * (1e6 / kSampleRate));
-
- // Log the status.
- if (20 == frame_count)
- EXPECT_EQ(EP_PRE_SPEECH, ep_status);
- if (70 == frame_count)
- EXPECT_EQ(EP_SPEECH_PRESENT, ep_status);
- if (120 == frame_count)
- EXPECT_EQ(EP_PRE_SPEECH, ep_status);
- }
-}
-
-// This test instantiates and initializes a stand alone endpointer module.
-// The test creates FrameData objects with random noise and send them
-// to the endointer module. The energy of the first 50 frames is low,
-// followed by 500 high energy frames, and another 50 low energy frames.
-// We test that the correct start and end frames were detected.
-class EnergyEndpointerFrameProcessor : public FrameProcessor {
- public:
- explicit EnergyEndpointerFrameProcessor(EnergyEndpointer* endpointer)
- : endpointer_(endpointer) {}
-
- EpStatus ProcessFrame(int64_t time,
- int16_t* samples,
- int /*frame_size*/) override {
- endpointer_->ProcessAudioFrame(time, samples, kFrameSize, NULL);
-
- int64_t ep_time;
- return endpointer_->Status(&ep_time);
- }
-
- private:
- EnergyEndpointer* endpointer_;
-};
-
-TEST(EndpointerTest, TestEnergyEndpointerEvents) {
- // Initialize endpointer and configure it. We specify the parameters
- // here for a 20ms window, and a 20ms step size, which corrsponds to
- // the narrow band AMR codec.
- EnergyEndpointerParams ep_config;
- ep_config.set_frame_period(1.0f / static_cast<float>(kFrameRate));
- ep_config.set_frame_duration(1.0f / static_cast<float>(kFrameRate));
- ep_config.set_endpoint_margin(0.2f);
- ep_config.set_onset_window(0.15f);
- ep_config.set_speech_on_window(0.4f);
- ep_config.set_offset_window(0.15f);
- ep_config.set_onset_detect_dur(0.09f);
- ep_config.set_onset_confirm_dur(0.075f);
- ep_config.set_on_maintain_dur(0.10f);
- ep_config.set_offset_confirm_dur(0.12f);
- ep_config.set_decision_threshold(100.0f);
- EnergyEndpointer endpointer;
- endpointer.Init(ep_config);
-
- endpointer.StartSession();
-
- EnergyEndpointerFrameProcessor frame_processor(&endpointer);
- RunEndpointerEventsTest(&frame_processor);
-
- endpointer.EndSession();
-}
-
-// Test endpointer wrapper class.
-class EndpointerFrameProcessor : public FrameProcessor {
- public:
- typedef ::media::ShellAudioBus ShellAudioBus;
- explicit EndpointerFrameProcessor(Endpointer* endpointer)
- : endpointer_(endpointer) {}
-
- EpStatus ProcessFrame(int64_t /*time*/,
- int16_t* samples,
- int /*frame_size*/) override {
- scoped_ptr<ShellAudioBus> frame(new ShellAudioBus(1, kFrameSize, samples));
- endpointer_->ProcessAudio(*frame, NULL);
-
- int64_t ep_time;
- return endpointer_->Status(&ep_time);
- }
-
- private:
- Endpointer* endpointer_;
-};
-
-TEST(EndpointerTest, TestEmbeddedEndpointerEvents) {
- Endpointer endpointer(kSampleRate);
- const int64_t kMillisecondsPerMicrosecond = 1000;
- const int64_t short_timeout = 300 * kMillisecondsPerMicrosecond;
- endpointer.set_speech_input_possibly_complete_silence_length(short_timeout);
- const int64_t long_timeout = 500 * kMillisecondsPerMicrosecond;
- endpointer.set_speech_input_complete_silence_length(long_timeout);
- endpointer.StartSession();
-
- EndpointerFrameProcessor frame_processor(&endpointer);
- RunEndpointerEventsTest(&frame_processor);
-
- endpointer.EndSession();
-}
-
-} // namespace speech
-} // namespace cobalt
diff --git a/src/cobalt/speech/endpointer/energy_endpointer.cc b/src/cobalt/speech/endpointer/energy_endpointer.cc
deleted file mode 100644
index bd7ca80..0000000
--- a/src/cobalt/speech/endpointer/energy_endpointer.cc
+++ /dev/null
@@ -1,379 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "cobalt/speech/endpointer/energy_endpointer.h"
-
-#include <math.h>
-#include <stddef.h>
-
-#include "base/logging.h"
-
-namespace {
-
-// Returns the RMS (quadratic mean) of the input signal.
-float RMS(const int16_t* samples, int num_samples) {
- int64_t ssq_int64 = 0;
- int64_t sum_int64 = 0;
- for (int i = 0; i < num_samples; ++i) {
- sum_int64 += samples[i];
- ssq_int64 += samples[i] * samples[i];
- }
- // now convert to floats.
- double sum = static_cast<double>(sum_int64);
- sum /= num_samples;
- double ssq = static_cast<double>(ssq_int64);
- return static_cast<float>(sqrt((ssq / num_samples) - (sum * sum)));
-}
-
-int64_t Secs2Usecs(float seconds) {
- return static_cast<int64_t>(0.5 + (1.0e6 * seconds));
-}
-
-float GetDecibel(float value) {
- if (value > 1.0e-100)
- return static_cast<float>(20 * log10(value));
- return -2000.0;
-}
-
-} // namespace
-
-namespace cobalt {
-namespace speech {
-
-// Stores threshold-crossing histories for making decisions about the speech
-// state.
-class EnergyEndpointer::HistoryRing {
- public:
- HistoryRing() : insertion_index_(0) {}
-
- // Resets the ring to |size| elements each with state |initial_state|
- void SetRing(int size, bool initial_state);
-
- // Inserts a new entry into the ring and drops the oldest entry.
- void Insert(int64_t time_us, bool decision);
-
- // Returns the time in microseconds of the most recently added entry.
- int64_t EndTime() const;
-
- // Returns the sum of all intervals during which 'decision' is true within
- // the time in seconds specified by 'duration'. The returned interval is
- // in seconds.
- float RingSum(float duration_sec);
-
- private:
- struct DecisionPoint {
- int64_t time_us;
- bool decision;
- };
-
- std::vector<DecisionPoint> decision_points_;
- int insertion_index_; // Index at which the next item gets added/inserted.
-
- DISALLOW_COPY_AND_ASSIGN(HistoryRing);
-};
-
-void EnergyEndpointer::HistoryRing::SetRing(int size, bool initial_state) {
- insertion_index_ = 0;
- decision_points_.clear();
- DecisionPoint init = { -1, initial_state };
- decision_points_.resize(static_cast<size_t>(size), init);
-}
-
-void EnergyEndpointer::HistoryRing::Insert(int64_t time_us, bool decision) {
- decision_points_[static_cast<size_t>(insertion_index_)].time_us = time_us;
- decision_points_[static_cast<size_t>(insertion_index_)].decision = decision;
- insertion_index_ =
- static_cast<int>((insertion_index_ + 1) % decision_points_.size());
-}
-
-int64_t EnergyEndpointer::HistoryRing::EndTime() const {
- int ind = insertion_index_ - 1;
- if (ind < 0)
- ind = static_cast<int>(decision_points_.size() - 1);
- return decision_points_[static_cast<size_t>(ind)].time_us;
-}
-
-float EnergyEndpointer::HistoryRing::RingSum(float duration_sec) {
- if (decision_points_.empty())
- return 0.0;
-
- int64_t sum_us = 0;
- int ind = insertion_index_ - 1;
- if (ind < 0)
- ind = static_cast<int>(decision_points_.size() - 1);
- int64_t end_us = decision_points_[static_cast<size_t>(ind)].time_us;
- bool is_on = decision_points_[static_cast<size_t>(ind)].decision;
- int64_t start_us =
- end_us - static_cast<int64_t>(0.5 + (1.0e6 * duration_sec));
- if (start_us < 0)
- start_us = 0;
- size_t n_summed = 1; // n points ==> (n-1) intervals
- while ((decision_points_[static_cast<size_t>(ind)].time_us > start_us) &&
- (n_summed < decision_points_.size())) {
- --ind;
- if (ind < 0)
- ind = static_cast<int>(decision_points_.size() - 1);
- if (is_on)
- sum_us += end_us - decision_points_[static_cast<size_t>(ind)].time_us;
- is_on = decision_points_[static_cast<size_t>(ind)].decision;
- end_us = decision_points_[static_cast<size_t>(ind)].time_us;
- n_summed++;
- }
-
- return 1.0e-6f * sum_us; // Returns total time that was super threshold.
-}
-
-EnergyEndpointer::EnergyEndpointer()
- : status_(EP_PRE_SPEECH),
- offset_confirm_dur_sec_(0),
- endpointer_time_us_(0),
- fast_update_frames_(0),
- frame_counter_(0),
- max_window_dur_(4.0),
- sample_rate_(0),
- history_(new HistoryRing()),
- decision_threshold_(0),
- estimating_environment_(false),
- noise_level_(0),
- rms_adapt_(0),
- start_lag_(0),
- end_lag_(0),
- user_input_start_time_us_(0) {
-}
-
-EnergyEndpointer::~EnergyEndpointer() {
-}
-
-int EnergyEndpointer::TimeToFrame(float time) const {
- return static_cast<int32_t>(0.5 + (time / params_.frame_period()));
-}
-
-void EnergyEndpointer::Restart(bool reset_threshold) {
- status_ = EP_PRE_SPEECH;
- user_input_start_time_us_ = 0;
-
- if (reset_threshold) {
- decision_threshold_ = params_.decision_threshold();
- rms_adapt_ = decision_threshold_;
- noise_level_ = params_.decision_threshold() / 2.0f;
- frame_counter_ = 0; // Used for rapid initial update of levels.
- }
-
- // Set up the memories to hold the history windows.
- history_->SetRing(TimeToFrame(max_window_dur_), false);
-
- // Flag that indicates that current input should be used for
- // estimating the environment. The user has not yet started input
- // by e.g. pressed the push-to-talk button. By default, this is
- // false for backward compatibility.
- estimating_environment_ = false;
-}
-
-void EnergyEndpointer::Init(const EnergyEndpointerParams& params) {
- params_ = params;
-
- // Find the longest history interval to be used, and make the ring
- // large enough to accommodate that number of frames. NOTE: This
- // depends upon ep_frame_period being set correctly in the factory
- // that did this instantiation.
- max_window_dur_ = params_.onset_window();
- if (params_.speech_on_window() > max_window_dur_)
- max_window_dur_ = params_.speech_on_window();
- if (params_.offset_window() > max_window_dur_)
- max_window_dur_ = params_.offset_window();
- Restart(true);
-
- offset_confirm_dur_sec_ = params_.offset_window() -
- params_.offset_confirm_dur();
- if (offset_confirm_dur_sec_ < 0.0)
- offset_confirm_dur_sec_ = 0.0;
-
- user_input_start_time_us_ = 0;
-
- // Flag that indicates that current input should be used for
- // estimating the environment. The user has not yet started input
- // by e.g. pressed the push-to-talk button. By default, this is
- // false for backward compatibility.
- estimating_environment_ = false;
- // The initial value of the noise and speech levels is inconsequential.
- // The level of the first frame will overwrite these values.
- noise_level_ = params_.decision_threshold() / 2.0f;
- fast_update_frames_ =
- static_cast<int64_t>(params_.fast_update_dur() / params_.frame_period());
-
- frame_counter_ = 0; // Used for rapid initial update of levels.
-
- sample_rate_ = params_.sample_rate();
- start_lag_ = static_cast<int>(sample_rate_ /
- params_.max_fundamental_frequency());
- end_lag_ = static_cast<int>(sample_rate_ /
- params_.min_fundamental_frequency());
-}
-
-void EnergyEndpointer::StartSession() {
- Restart(true);
-}
-
-void EnergyEndpointer::EndSession() {
- status_ = EP_POST_SPEECH;
-}
-
-void EnergyEndpointer::SetEnvironmentEstimationMode() {
- Restart(true);
- estimating_environment_ = true;
-}
-
-void EnergyEndpointer::SetUserInputMode() {
- estimating_environment_ = false;
- user_input_start_time_us_ = endpointer_time_us_;
-}
-
-void EnergyEndpointer::ProcessAudioFrame(int64_t time_us,
- const int16_t* samples,
- int num_samples,
- float* rms_out) {
- endpointer_time_us_ = time_us;
- float rms = RMS(samples, num_samples);
-
- // Check that this is user input audio vs. pre-input adaptation audio.
- // Input audio starts when the user indicates start of input, by e.g.
- // pressing push-to-talk. Audio received prior to that is used to update
- // noise and speech level estimates.
- if (!estimating_environment_) {
- bool decision = false;
- if ((endpointer_time_us_ - user_input_start_time_us_) <
- Secs2Usecs(params_.contamination_rejection_period())) {
- decision = false;
- DVLOG(1) << "decision: forced to false, time: " << endpointer_time_us_;
- } else {
- decision = (rms > decision_threshold_);
- }
-
- history_->Insert(endpointer_time_us_, decision);
-
- switch (status_) {
- case EP_PRE_SPEECH:
- if (history_->RingSum(params_.onset_window()) >
- params_.onset_detect_dur()) {
- status_ = EP_POSSIBLE_ONSET;
- }
- break;
-
- case EP_POSSIBLE_ONSET: {
- float tsum = history_->RingSum(params_.onset_window());
- if (tsum > params_.onset_confirm_dur()) {
- status_ = EP_SPEECH_PRESENT;
- } else { // If signal is not maintained, drop back to pre-speech.
- if (tsum <= params_.onset_detect_dur())
- status_ = EP_PRE_SPEECH;
- }
- break;
- }
-
- case EP_SPEECH_PRESENT: {
- // To induce hysteresis in the state residency, we allow a
- // smaller residency time in the on_ring, than was required to
- // enter the SPEECH_PERSENT state.
- float on_time = history_->RingSum(params_.speech_on_window());
- if (on_time < params_.on_maintain_dur())
- status_ = EP_POSSIBLE_OFFSET;
- break;
- }
-
- case EP_POSSIBLE_OFFSET:
- if (history_->RingSum(params_.offset_window()) <=
- offset_confirm_dur_sec_) {
- // Note that this offset time may be beyond the end
- // of the input buffer in a real-time system. It will be up
- // to the RecognizerSession to decide what to do.
- status_ = EP_PRE_SPEECH; // Automatically reset for next utterance.
- } else { // If speech picks up again we allow return to SPEECH_PRESENT.
- if (history_->RingSum(params_.speech_on_window()) >=
- params_.on_maintain_dur())
- status_ = EP_SPEECH_PRESENT;
- }
- break;
-
- case EP_POST_SPEECH:
- // fall-through
- default:
- LOG(WARNING) << "Invalid case in switch: " << status_;
- break;
- }
-
- // If this is a quiet, non-speech region, slowly adapt the detection
- // threshold to be about 6dB above the average RMS.
- if ((!decision) && (status_ == EP_PRE_SPEECH)) {
- decision_threshold_ = (0.98f * decision_threshold_) + (0.02f * 2 * rms);
- rms_adapt_ = decision_threshold_;
- } else {
- // If this is in a speech region, adapt the decision threshold to
- // be about 10dB below the average RMS. If the noise level is high,
- // the threshold is pushed up.
- // Adaptation up to a higher level is 5 times faster than decay to
- // a lower level.
- if ((status_ == EP_SPEECH_PRESENT) && decision) {
- if (rms_adapt_ > rms) {
- rms_adapt_ = (0.99f * rms_adapt_) + (0.01f * rms);
- } else {
- rms_adapt_ = (0.95f * rms_adapt_) + (0.05f * rms);
- }
- float target_threshold = 0.3f * rms_adapt_ + noise_level_;
- decision_threshold_ = (.90f * decision_threshold_) +
- (0.10f * target_threshold);
- }
- }
-
- // Set a floor
- if (decision_threshold_ < params_.min_decision_threshold())
- decision_threshold_ = params_.min_decision_threshold();
- }
-
- // Update speech and noise levels.
- UpdateLevels(rms);
- ++frame_counter_;
-
- if (rms_out)
- *rms_out = GetDecibel(rms);
-}
-
-float EnergyEndpointer::GetNoiseLevelDb() const {
- return GetDecibel(noise_level_);
-}
-
-void EnergyEndpointer::UpdateLevels(float rms) {
- // Update quickly initially. We assume this is noise and that
- // speech is 6dB above the noise.
- if (frame_counter_ < fast_update_frames_) {
- // Alpha increases from 0 to (k-1)/k where k is the number of time
- // steps in the initial adaptation period.
- float alpha = static_cast<float>(frame_counter_) /
- static_cast<float>(fast_update_frames_);
- noise_level_ = (alpha * noise_level_) + ((1 - alpha) * rms);
- DVLOG(1) << "FAST UPDATE, frame_counter_ " << frame_counter_
- << ", fast_update_frames_ " << fast_update_frames_;
- } else {
- // Update Noise level. The noise level adapts quickly downward, but
- // slowly upward. The noise_level_ parameter is not currently used
- // for threshold adaptation. It is used for UI feedback.
- if (noise_level_ < rms)
- noise_level_ = (0.999f * noise_level_) + (0.001f * rms);
- else
- noise_level_ = (0.95f * noise_level_) + (0.05f * rms);
- }
- if (estimating_environment_ || (frame_counter_ < fast_update_frames_)) {
- decision_threshold_ = noise_level_ * 2; // 6dB above noise level.
- // Set a floor
- if (decision_threshold_ < params_.min_decision_threshold())
- decision_threshold_ = params_.min_decision_threshold();
- }
-}
-
-EpStatus EnergyEndpointer::Status(int64_t* status_time) const {
- *status_time = history_->EndTime();
- return status_;
-}
-
-} // namespace speech
-} // namespace cobalt
diff --git a/src/cobalt/speech/endpointer/energy_endpointer.h b/src/cobalt/speech/endpointer/energy_endpointer.h
deleted file mode 100644
index d3822ea..0000000
--- a/src/cobalt/speech/endpointer/energy_endpointer.h
+++ /dev/null
@@ -1,162 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// The EnergyEndpointer class finds likely speech onset and offset points.
-//
-// The implementation described here is about the simplest possible.
-// It is based on timings of threshold crossings for overall signal
-// RMS. It is suitable for light weight applications.
-//
-// As written, the basic idea is that one specifies intervals that
-// must be occupied by super- and sub-threshold energy levels, and
-// defers decisions re onset and offset times until these
-// specifications have been met. Three basic intervals are tested: an
-// onset window, a speech-on window, and an offset window. We require
-// super-threshold to exceed some mimimum total durations in the onset
-// and speech-on windows before declaring the speech onset time, and
-// we specify a required sub-threshold residency in the offset window
-// before declaring speech offset. As the various residency requirements are
-// met, the EnergyEndpointer instance assumes various states, and can return the
-// ID of these states to the client (see EpStatus below).
-//
-// The levels of the speech and background noise are continuously updated. It is
-// important that the background noise level be estimated initially for
-// robustness in noisy conditions. The first frames are assumed to be background
-// noise and a fast update rate is used for the noise level. The duration for
-// fast update is controlled by the fast_update_dur_ paramter.
-//
-// If used in noisy conditions, the endpointer should be started and run in the
-// EnvironmentEstimation mode, for at least 200ms, before switching to
-// UserInputMode.
-// Audio feedback contamination can appear in the input audio, if not cut
-// out or handled by echo cancellation. Audio feedback can trigger a false
-// accept. The false accepts can be ignored by setting
-// ep_contamination_rejection_period.
-
-#ifndef COBALT_SPEECH_ENDPOINTER_ENERGY_ENDPOINTER_H_
-#define COBALT_SPEECH_ENDPOINTER_ENERGY_ENDPOINTER_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <vector>
-
-#include "base/memory/scoped_ptr.h"
-#include "cobalt/speech/endpointer/energy_endpointer_params.h"
-
-namespace cobalt {
-namespace speech {
-
-// Endpointer status codes
-enum EpStatus {
- EP_PRE_SPEECH = 10,
- EP_POSSIBLE_ONSET,
- EP_SPEECH_PRESENT,
- EP_POSSIBLE_OFFSET,
- EP_POST_SPEECH,
-};
-
-class EnergyEndpointer {
- public:
- // The default construction MUST be followed by Init(), before any
- // other use can be made of the instance.
- EnergyEndpointer();
- virtual ~EnergyEndpointer();
-
- void Init(const EnergyEndpointerParams& params);
-
- // Start the endpointer. This should be called at the beginning of a session.
- void StartSession();
-
- // Stop the endpointer.
- void EndSession();
-
- // Start environment estimation. Audio will be used for environment estimation
- // i.e. noise level estimation.
- void SetEnvironmentEstimationMode();
-
- // Start user input. This should be called when the user indicates start of
- // input, e.g. by pressing a button.
- void SetUserInputMode();
-
- // Computes the next input frame and modifies EnergyEndpointer status as
- // appropriate based on the computation.
- void ProcessAudioFrame(int64_t time_us,
- const int16_t* samples,
- int num_samples,
- float* rms_out);
-
- // Returns the current state of the EnergyEndpointer and the time
- // corresponding to the most recently computed frame.
- EpStatus Status(int64_t* status_time_us) const;
-
- bool estimating_environment() const {
- return estimating_environment_;
- }
-
- // Returns estimated noise level in dB.
- float GetNoiseLevelDb() const;
-
- private:
- class HistoryRing;
-
- // Resets the endpointer internal state. If reset_threshold is true, the
- // state will be reset completely, including adaptive thresholds and the
- // removal of all history information.
- void Restart(bool reset_threshold);
-
- // Update internal speech and noise levels.
- void UpdateLevels(float rms);
-
- // Returns the number of frames (or frame number) corresponding to
- // the 'time' (in seconds).
- int TimeToFrame(float time) const;
-
- EpStatus status_; // The current state of this instance.
- float offset_confirm_dur_sec_; // max on time allowed to confirm POST_SPEECH
- int64_t
- endpointer_time_us_; // Time of the most recently received audio frame.
- int64_t
- fast_update_frames_; // Number of frames for initial level adaptation.
- int64_t
- frame_counter_; // Number of frames seen. Used for initial adaptation.
- float max_window_dur_; // Largest search window size (seconds)
- float sample_rate_; // Sampling rate.
-
- // Ring buffers to hold the speech activity history.
- scoped_ptr<HistoryRing> history_;
-
- // Configuration parameters.
- EnergyEndpointerParams params_;
-
- // RMS which must be exceeded to conclude frame is speech.
- float decision_threshold_;
-
- // Flag to indicate that audio should be used to estimate environment, prior
- // to receiving user input.
- bool estimating_environment_;
-
- // Estimate of the background noise level. Used externally for UI feedback.
- float noise_level_;
-
- // An adaptive threshold used to update decision_threshold_ when appropriate.
- float rms_adapt_;
-
- // Start lag corresponds to the highest fundamental frequency.
- int start_lag_;
-
- // End lag corresponds to the lowest fundamental frequency.
- int end_lag_;
-
- // Time when mode switched from environment estimation to user input. This
- // is used to time forced rejection of audio feedback contamination.
- int64_t user_input_start_time_us_;
-
- DISALLOW_COPY_AND_ASSIGN(EnergyEndpointer);
-};
-
-} // namespace speech
-} // namespace cobalt
-
-#endif // COBALT_SPEECH_ENDPOINTER_ENERGY_ENDPOINTER_H_
diff --git a/src/cobalt/speech/endpointer/energy_endpointer_params.cc b/src/cobalt/speech/endpointer/energy_endpointer_params.cc
deleted file mode 100644
index c71e11c..0000000
--- a/src/cobalt/speech/endpointer/energy_endpointer_params.cc
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "cobalt/speech/endpointer/energy_endpointer_params.h"
-
-namespace cobalt {
-namespace speech {
-
-EnergyEndpointerParams::EnergyEndpointerParams() {
- SetDefaults();
-}
-
-void EnergyEndpointerParams::SetDefaults() {
- frame_period_ = 0.01f;
- frame_duration_ = 0.01f;
- endpoint_margin_ = 0.2f;
- onset_window_ = 0.15f;
- speech_on_window_ = 0.4f;
- offset_window_ = 0.15f;
- onset_detect_dur_ = 0.09f;
- onset_confirm_dur_ = 0.075f;
- on_maintain_dur_ = 0.10f;
- offset_confirm_dur_ = 0.12f;
- decision_threshold_ = 150.0f;
- min_decision_threshold_ = 50.0f;
- fast_update_dur_ = 0.2f;
- sample_rate_ = 8000.0f;
- min_fundamental_frequency_ = 57.143f;
- max_fundamental_frequency_ = 400.0f;
- contamination_rejection_period_ = 0.25f;
-}
-
-void EnergyEndpointerParams::operator=(const EnergyEndpointerParams& source) {
- frame_period_ = source.frame_period();
- frame_duration_ = source.frame_duration();
- endpoint_margin_ = source.endpoint_margin();
- onset_window_ = source.onset_window();
- speech_on_window_ = source.speech_on_window();
- offset_window_ = source.offset_window();
- onset_detect_dur_ = source.onset_detect_dur();
- onset_confirm_dur_ = source.onset_confirm_dur();
- on_maintain_dur_ = source.on_maintain_dur();
- offset_confirm_dur_ = source.offset_confirm_dur();
- decision_threshold_ = source.decision_threshold();
- min_decision_threshold_ = source.min_decision_threshold();
- fast_update_dur_ = source.fast_update_dur();
- sample_rate_ = source.sample_rate();
- min_fundamental_frequency_ = source.min_fundamental_frequency();
- max_fundamental_frequency_ = source.max_fundamental_frequency();
- contamination_rejection_period_ = source.contamination_rejection_period();
-}
-
-} // namespace speech
-} // namespace cobalt
diff --git a/src/cobalt/speech/endpointer/energy_endpointer_params.h b/src/cobalt/speech/endpointer/energy_endpointer_params.h
deleted file mode 100644
index 06810d0..0000000
--- a/src/cobalt/speech/endpointer/energy_endpointer_params.h
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COBALT_SPEECH_ENDPOINTER_ENERGY_ENDPOINTER_PARAMS_H_
-#define COBALT_SPEECH_ENDPOINTER_ENERGY_ENDPOINTER_PARAMS_H_
-
-namespace cobalt {
-namespace speech {
-
-// Input parameters for the EnergyEndpointer class.
-class EnergyEndpointerParams {
- public:
- EnergyEndpointerParams();
-
- void SetDefaults();
-
- void operator=(const EnergyEndpointerParams& source);
-
- // Accessors and mutators
- float frame_period() const { return frame_period_; }
- void set_frame_period(float frame_period) {
- frame_period_ = frame_period;
- }
-
- float frame_duration() const { return frame_duration_; }
- void set_frame_duration(float frame_duration) {
- frame_duration_ = frame_duration;
- }
-
- float endpoint_margin() const { return endpoint_margin_; }
- void set_endpoint_margin(float endpoint_margin) {
- endpoint_margin_ = endpoint_margin;
- }
-
- float onset_window() const { return onset_window_; }
- void set_onset_window(float onset_window) { onset_window_ = onset_window; }
-
- float speech_on_window() const { return speech_on_window_; }
- void set_speech_on_window(float speech_on_window) {
- speech_on_window_ = speech_on_window;
- }
-
- float offset_window() const { return offset_window_; }
- void set_offset_window(float offset_window) {
- offset_window_ = offset_window;
- }
-
- float onset_detect_dur() const { return onset_detect_dur_; }
- void set_onset_detect_dur(float onset_detect_dur) {
- onset_detect_dur_ = onset_detect_dur;
- }
-
- float onset_confirm_dur() const { return onset_confirm_dur_; }
- void set_onset_confirm_dur(float onset_confirm_dur) {
- onset_confirm_dur_ = onset_confirm_dur;
- }
-
- float on_maintain_dur() const { return on_maintain_dur_; }
- void set_on_maintain_dur(float on_maintain_dur) {
- on_maintain_dur_ = on_maintain_dur;
- }
-
- float offset_confirm_dur() const { return offset_confirm_dur_; }
- void set_offset_confirm_dur(float offset_confirm_dur) {
- offset_confirm_dur_ = offset_confirm_dur;
- }
-
- float decision_threshold() const { return decision_threshold_; }
- void set_decision_threshold(float decision_threshold) {
- decision_threshold_ = decision_threshold;
- }
-
- float min_decision_threshold() const { return min_decision_threshold_; }
- void set_min_decision_threshold(float min_decision_threshold) {
- min_decision_threshold_ = min_decision_threshold;
- }
-
- float fast_update_dur() const { return fast_update_dur_; }
- void set_fast_update_dur(float fast_update_dur) {
- fast_update_dur_ = fast_update_dur;
- }
-
- float sample_rate() const { return sample_rate_; }
- void set_sample_rate(float sample_rate) { sample_rate_ = sample_rate; }
-
- float min_fundamental_frequency() const { return min_fundamental_frequency_; }
- void set_min_fundamental_frequency(float min_fundamental_frequency) {
- min_fundamental_frequency_ = min_fundamental_frequency;
- }
-
- float max_fundamental_frequency() const { return max_fundamental_frequency_; }
- void set_max_fundamental_frequency(float max_fundamental_frequency) {
- max_fundamental_frequency_ = max_fundamental_frequency;
- }
-
- float contamination_rejection_period() const {
- return contamination_rejection_period_;
- }
- void set_contamination_rejection_period(
- float contamination_rejection_period) {
- contamination_rejection_period_ = contamination_rejection_period;
- }
-
- private:
- float frame_period_; // Frame period
- float frame_duration_; // Window size
- float onset_window_; // Interval scanned for onset activity
- float speech_on_window_; // Inverval scanned for ongoing speech
- float offset_window_; // Interval scanned for offset evidence
- float offset_confirm_dur_; // Silence duration required to confirm offset
- float decision_threshold_; // Initial rms detection threshold
- float min_decision_threshold_; // Minimum rms detection threshold
- float fast_update_dur_; // Period for initial estimation of levels.
- float sample_rate_; // Expected sample rate.
-
- // Time to add on either side of endpoint threshold crossings
- float endpoint_margin_;
- // Total dur within onset_window required to enter ONSET state
- float onset_detect_dur_;
- // Total on time within onset_window required to enter SPEECH_ON state
- float onset_confirm_dur_;
- // Minimum dur in SPEECH_ON state required to maintain ON state
- float on_maintain_dur_;
- // Minimum fundamental frequency for autocorrelation.
- float min_fundamental_frequency_;
- // Maximum fundamental frequency for autocorrelation.
- float max_fundamental_frequency_;
- // Period after start of user input that above threshold values are ignored.
- // This is to reject audio feedback contamination.
- float contamination_rejection_period_;
-};
-
-} // namespace speech
-} // namespace cobalt
-
-#endif // COBALT_SPEECH_ENDPOINTER_ENERGY_ENDPOINTER_PARAMS_H_
diff --git a/src/cobalt/speech/endpointer_delegate.cc b/src/cobalt/speech/endpointer_delegate.cc
index a3d0444..f8a826f 100644
--- a/src/cobalt/speech/endpointer_delegate.cc
+++ b/src/cobalt/speech/endpointer_delegate.cc
@@ -63,8 +63,8 @@
endpointer_.ProcessAudio(audio_bus, NULL);
int64_t ep_time;
- EpStatus status = endpointer_.Status(&ep_time);
- if (status == EP_POSSIBLE_ONSET) {
+ content::EpStatus status = endpointer_.Status(&ep_time);
+ if (status == content::EP_POSSIBLE_ONSET) {
is_first_time_sound_started_ = true;
}
diff --git a/src/cobalt/speech/endpointer_delegate.h b/src/cobalt/speech/endpointer_delegate.h
index 8a3aafa..db6a528 100644
--- a/src/cobalt/speech/endpointer_delegate.h
+++ b/src/cobalt/speech/endpointer_delegate.h
@@ -17,7 +17,7 @@
#ifndef COBALT_SPEECH_ENDPOINTER_DELEGATE_H_
#define COBALT_SPEECH_ENDPOINTER_DELEGATE_H_
-#include "cobalt/speech/endpointer/endpointer.h"
+#include "content/browser/speech/endpointer/endpointer.h"
#include "media/base/audio_bus.h"
namespace cobalt {
@@ -42,7 +42,7 @@
private:
// Used for detecting sound start event.
- Endpointer endpointer_;
+ content::Endpointer endpointer_;
// Used for recording the number of samples before notifying that it is the
// first time the sound started. The |endpointer_| should be started and run
// in the EnvironmentEstimation mode if used in noisy conditions for at least
diff --git a/src/cobalt/speech/microphone_manager.cc b/src/cobalt/speech/microphone_manager.cc
index 5d8210d..ea3ba7d 100644
--- a/src/cobalt/speech/microphone_manager.cc
+++ b/src/cobalt/speech/microphone_manager.cc
@@ -166,13 +166,14 @@
static int16_t samples[kBufferSizeInBytes / sizeof(int16_t)];
int read_bytes =
microphone_->Read(reinterpret_cast<char*>(samples), kBufferSizeInBytes);
- if (read_bytes > 0) {
+ // If |read_bytes| is zero, nothing should happen.
+ if (read_bytes > 0 && read_bytes % sizeof(int16_t) == 0) {
size_t frames = read_bytes / sizeof(int16_t);
scoped_ptr<ShellAudioBus> output_audio_bus(new ShellAudioBus(
1, frames, ShellAudioBus::kInt16, ShellAudioBus::kInterleaved));
output_audio_bus->Assign(ShellAudioBus(1, frames, samples));
data_received_callback_.Run(output_audio_bus.Pass());
- } else if (read_bytes < 0) {
+ } else if (read_bytes != 0) {
state_ = kError;
error_callback_.Run(new SpeechRecognitionError(
SpeechRecognitionError::kAborted, "Microphone read failed."));
diff --git a/src/cobalt/speech/speech.gyp b/src/cobalt/speech/speech.gyp
index aa933d3..4d431dd 100644
--- a/src/cobalt/speech/speech.gyp
+++ b/src/cobalt/speech/speech.gyp
@@ -28,18 +28,8 @@
'sources': [
'audio_encoder_flac.cc',
'audio_encoder_flac.h',
- 'chunked_byte_buffer.cc',
- 'chunked_byte_buffer.h',
'endpointer_delegate.cc',
'endpointer_delegate.h',
-
- 'endpointer/endpointer.cc',
- 'endpointer/endpointer.h',
- 'endpointer/energy_endpointer.cc',
- 'endpointer/energy_endpointer.h',
- 'endpointer/energy_endpointer_params.cc',
- 'endpointer/energy_endpointer_params.h',
-
'google_streaming_api.pb.cc',
'google_streaming_api.pb.h',
'google_streaming_api.pb.proto',
@@ -68,6 +58,7 @@
'dependencies': [
'<(DEPTH)/cobalt/base/base.gyp:base',
'<(DEPTH)/cobalt/dom/dom.gyp:dom',
+ '<(DEPTH)/content/browser/speech/speech.gyp:speech',
'<(DEPTH)/third_party/flac/flac.gyp:libflac',
'<(DEPTH)/third_party/protobuf/protobuf.gyp:protobuf_lite',
],
@@ -112,30 +103,5 @@
],
],
},
- {
- 'target_name': 'speech_test',
- 'type': '<(gtest_target_type)',
- 'sources': [
- 'chunked_byte_buffer_unittest.cc',
- 'endpointer/endpointer_unittest.cc',
- ],
- 'dependencies': [
- '<(DEPTH)/base/base.gyp:run_all_unittests',
- '<(DEPTH)/cobalt/speech/speech.gyp:speech',
- '<(DEPTH)/testing/gtest.gyp:gtest',
- ],
- },
-
- {
- 'target_name': 'speech_test_deploy',
- 'type': 'none',
- 'dependencies': [
- 'speech_test',
- ],
- 'variables': {
- 'executable_name': 'speech_test',
- },
- 'includes': [ '../../starboard/build/deploy.gypi' ],
- },
],
}
diff --git a/src/cobalt/speech/speech_recognizer.cc b/src/cobalt/speech/speech_recognizer.cc
index 27eb43a..902fd48 100644
--- a/src/cobalt/speech/speech_recognizer.cc
+++ b/src/cobalt/speech/speech_recognizer.cc
@@ -166,14 +166,17 @@
SpeechRecognizer::SpeechRecognizer(network::NetworkModule* network_module,
const EventCallback& event_callback)
: network_module_(network_module),
- thread_("speech_recognizer"),
started_(false),
- event_callback_(event_callback) {
+ event_callback_(event_callback),
+ thread_("speech_recognizer") {
thread_.StartWithOptions(base::Thread::Options(MessageLoop::TYPE_IO, 0));
}
SpeechRecognizer::~SpeechRecognizer() {
Stop();
+ // Stopping the thread here to ensure that StopInternal has completed before
+ // we finish running the destructor.
+ thread_.Stop();
}
void SpeechRecognizer::Start(const SpeechRecognitionConfig& config,
@@ -236,7 +239,7 @@
void SpeechRecognizer::OnURLFetchComplete(const net::URLFetcher* source) {
DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
UNREFERENCED_PARAMETER(source);
- started_ = false;
+ // no-op.
}
void SpeechRecognizer::StartInternal(const SpeechRecognitionConfig& config,
diff --git a/src/cobalt/speech/speech_recognizer.h b/src/cobalt/speech/speech_recognizer.h
index 90e5dcd..55cd68f 100644
--- a/src/cobalt/speech/speech_recognizer.h
+++ b/src/cobalt/speech/speech_recognizer.h
@@ -23,9 +23,9 @@
#include "base/threading/thread.h"
#include "cobalt/network/network_module.h"
#include "cobalt/speech/audio_encoder_flac.h"
-#include "cobalt/speech/chunked_byte_buffer.h"
#include "cobalt/speech/speech_recognition_config.h"
#include "cobalt/speech/speech_recognition_event.h"
+#include "content/browser/speech/chunked_byte_buffer.h"
#include "media/base/audio_bus.h"
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_fetcher_delegate.h"
@@ -77,8 +77,6 @@
// This is used for creating fetchers.
network::NetworkModule* network_module_;
- // Speech recognizer is operating in its own thread.
- base::Thread thread_;
// Track the start/stop state of speech recognizer.
bool started_;
@@ -90,9 +88,11 @@
scoped_ptr<net::URLFetcher> downstream_fetcher_;
EventCallback event_callback_;
// Used for processing proto buffer data.
- ChunkedByteBuffer chunked_byte_buffer_;
+ content::ChunkedByteBuffer chunked_byte_buffer_;
// Used for accumulating final results.
SpeechRecognitionResults final_results_;
+ // Speech recognizer is operating in its own thread.
+ base::Thread thread_;
};
} // namespace speech
diff --git a/src/cobalt/storage/storage_manager.cc b/src/cobalt/storage/storage_manager.cc
index decba88..79a5c8f 100644
--- a/src/cobalt/storage/storage_manager.cc
+++ b/src/cobalt/storage/storage_manager.cc
@@ -275,11 +275,9 @@
sizeof(VirtualFileSystem::SerializedHeader));
}
- if (VirtualFileSystem::GetHeaderVersion(header) == -1) {
+ if (!vfs_->Deserialize(&raw_bytes[0], buffer_size)) {
VirtualFile* vf = vfs_->Open(kDefaultSaveFile);
vf->Write(&raw_bytes[0], buffer_size, 0 /* offset */);
- } else {
- vfs_->Deserialize(&raw_bytes[0], buffer_size);
}
}
}
diff --git a/src/cobalt/storage/virtual_file.cc b/src/cobalt/storage/virtual_file.cc
index ee738da..64904c2 100644
--- a/src/cobalt/storage/virtual_file.cc
+++ b/src/cobalt/storage/virtual_file.cc
@@ -27,60 +27,74 @@
// We must use forward declarations to be able to specify the
// WARN_UNUSED_RESULT attribute.
-// Read size bytes from buffer into dest. Return # of bytes read.
-int ReadBuffer(uint8* dest, const uint8* buffer, int size) WARN_UNUSED_RESULT;
+// Read size bytes from buffer into dest. Updates buffer and buffer_remaining
+// on success.
+// Returns true on success, false on buffer overrun
+bool ReadBuffer(uint8* dest, const uint8** buffer, size_t size,
+ size_t* buffer_remaining) WARN_UNUSED_RESULT;
// Write size bytes from source into buffer. Return # of bytes written.
-int WriteBuffer(uint8* buffer, const uint8* source, int size,
- bool dry_run) WARN_UNUSED_RESULT;
+size_t WriteBuffer(uint8* buffer, const uint8* source, size_t size,
+ bool dry_run) WARN_UNUSED_RESULT;
-int ReadBuffer(uint8* dest, const uint8* buffer, int size) {
- memcpy(dest, buffer, static_cast<size_t>(size));
- return size;
+bool ReadBuffer(uint8* dest, const uint8** buffer, size_t size,
+ size_t* buffer_remaining) {
+ if (size > *buffer_remaining) {
+ return false;
+ }
+ memcpy(dest, *buffer, size);
+ *buffer_remaining -= size;
+ *buffer += size;
+ return true;
}
-int WriteBuffer(uint8* buffer, const uint8* source, int size, bool dry_run) {
+size_t WriteBuffer(uint8* buffer, const uint8* source, size_t size,
+ bool dry_run) {
if (!dry_run) {
- memcpy(buffer, source, static_cast<size_t>(size));
+ memcpy(buffer, source, size);
}
return size;
}
} // namespace
-VirtualFile::VirtualFile(const std::string& name) : size_(0), name_(name) {}
+VirtualFile::VirtualFile(const std::string& name) : name_(name) {}
VirtualFile::~VirtualFile() {}
-int VirtualFile::Read(void* dest, int bytes, int offset) const {
- DCHECK_GE(offset, 0);
- if (offset > size_) {
+int VirtualFile::Read(void* dest, int bytes_in, int offset_in) const {
+ DCHECK_GE(offset_in, 0);
+ DCHECK_GE(bytes_in, 0);
+ size_t offset = static_cast<size_t>(offset_in);
+ size_t bytes = static_cast<size_t>(bytes_in);
+ size_t size = buffer_.size();
+ if (offset > size) {
return 0;
}
- int bytes_to_read = std::min(size_ - offset, bytes);
+ size_t bytes_to_read = std::min(static_cast<size_t>(size - offset), bytes);
if (bytes_to_read == 0) {
return 0;
}
- memcpy(dest, &buffer_[static_cast<size_t>(offset)],
- static_cast<size_t>(bytes_to_read));
- return bytes_to_read;
+ memcpy(dest, &buffer_[offset], bytes_to_read);
+ return static_cast<int>(bytes_to_read);
}
-int VirtualFile::Write(const void* source, int bytes, int offset) {
- DCHECK_GE(offset, 0);
- DCHECK_GE(bytes, 0);
- if (static_cast<int>(buffer_.size()) < offset + bytes) {
- buffer_.resize(static_cast<size_t>(offset + bytes));
+int VirtualFile::Write(const void* source, int bytes_in, int offset_in) {
+ DCHECK_GE(offset_in, 0);
+ DCHECK_GE(bytes_in, 0);
+ size_t bytes = static_cast<size_t>(bytes_in);
+ size_t offset = static_cast<size_t>(offset_in);
+ if (buffer_.size() < offset + bytes) {
+ buffer_.resize(offset + bytes);
}
- memcpy(&buffer_[static_cast<size_t>(offset)], source,
- static_cast<size_t>(bytes));
- size_ = std::max(size_, offset + bytes);
- return bytes;
+ memcpy(&buffer_[offset], source, bytes);
+ return bytes_in;
}
int VirtualFile::Truncate(int size) {
- size_ = std::min(size_, size);
- return size_;
+ size_t newsize = std::min(buffer_.size(), static_cast<size_t>(size));
+ buffer_.resize(newsize);
+ return static_cast<int>(newsize);
}
int VirtualFile::Serialize(uint8* dest, bool dry_run) {
@@ -96,29 +110,36 @@
// Write the filename
cur_buffer +=
WriteBuffer(cur_buffer, reinterpret_cast<const uint8*>(name_.c_str()),
- static_cast<int>(name_length), dry_run);
+ static_cast<size_t>(name_length), dry_run);
// NOTE: Ensure the file size is 64-bit for compatibility
// with any existing serialized files.
- uint64 file_size = static_cast<uint64>(size_);
+ uint64 file_size = static_cast<uint64>(buffer_.size());
// Write the file contents size
cur_buffer +=
WriteBuffer(cur_buffer, reinterpret_cast<const uint8*>(&file_size),
sizeof(file_size), dry_run);
// Write the file contents
- cur_buffer += WriteBuffer(cur_buffer, &buffer_[0], size_, dry_run);
+ if (!buffer_.empty()) {
+ // std::vector does not define access to underlying array when empty
+ cur_buffer += WriteBuffer(cur_buffer, &buffer_[0], buffer_.size(), dry_run);
+ }
// Return the number of bytes written
return static_cast<int>(std::distance(dest, cur_buffer));
}
-int VirtualFile::Deserialize(const uint8* source) {
+int VirtualFile::Deserialize(const uint8* source, size_t buffer_remaining) {
// Read in filename length
const uint8* cur_buffer = source;
uint64 name_length;
- cur_buffer += ReadBuffer(reinterpret_cast<uint8*>(&name_length), cur_buffer,
- sizeof(name_length));
+ bool success = ReadBuffer(reinterpret_cast<uint8*>(&name_length), &cur_buffer,
+ sizeof(name_length), &buffer_remaining);
+ if (!success) {
+ DLOG(ERROR) << "Buffer overrun";
+ return -1;
+ }
if (name_length >= kMaxVfsPathname) {
DLOG(ERROR) << "Filename was longer than the maximum allowed.";
@@ -126,19 +147,34 @@
}
// Read in filename
char name[kMaxVfsPathname];
- cur_buffer += ReadBuffer(reinterpret_cast<uint8*>(name), cur_buffer,
- static_cast<int>(name_length));
+ success = ReadBuffer(reinterpret_cast<uint8*>(name), &cur_buffer,
+ static_cast<size_t>(name_length), &buffer_remaining);
+ if (!success) {
+ DLOG(ERROR) << "Buffer overrun";
+ return -1;
+ }
name_.assign(name, name_length);
// Read in file contents size.
uint64 file_size;
- cur_buffer += ReadBuffer(reinterpret_cast<uint8*>(&file_size), cur_buffer,
- sizeof(file_size));
- size_ = static_cast<int>(file_size);
-
+ success = ReadBuffer(reinterpret_cast<uint8*>(&file_size), &cur_buffer,
+ sizeof(file_size), &buffer_remaining);
+ if (!success) {
+ DLOG(ERROR) << "Buffer overrun";
+ return -1;
+ }
// Read in the file contents
- buffer_.resize(static_cast<size_t>(size_));
- cur_buffer += ReadBuffer(&buffer_[0], cur_buffer, size_);
+ buffer_.resize(static_cast<size_t>(file_size));
+
+ if (!buffer_.empty()) {
+ // std::vector does not define access to underlying array when empty
+ success =
+ ReadBuffer(&buffer_[0], &cur_buffer, buffer_.size(), &buffer_remaining);
+ if (!success) {
+ DLOG(ERROR) << "Buffer overrun";
+ return -1;
+ }
+ }
// Return the number of bytes read
return static_cast<int>(std::distance(source, cur_buffer));
diff --git a/src/cobalt/storage/virtual_file.h b/src/cobalt/storage/virtual_file.h
index 5741fd0..f5a9aec 100644
--- a/src/cobalt/storage/virtual_file.h
+++ b/src/cobalt/storage/virtual_file.h
@@ -41,7 +41,7 @@
int Read(void* dest, int bytes, int offset) const;
int Write(const void* source, int bytes, int offset);
int Truncate(int size);
- int Size() const { return size_; }
+ int Size() const { return static_cast<int>(buffer_.size()); }
private:
explicit VirtualFile(const std::string& name);
@@ -49,10 +49,12 @@
// Returns the number of bytes written
int Serialize(uint8* dest, const bool dry_run);
- int Deserialize(const uint8* source);
+ // Deserializes a file, returning the size of the file or
+ // < 0 on error.
+ // |buffer_remaining| is the maximum size of |source|
+ int Deserialize(const uint8* source, size_t buffer_remaining);
std::vector<uint8> buffer_;
- int size_;
std::string name_;
diff --git a/src/cobalt/storage/virtual_file_system.cc b/src/cobalt/storage/virtual_file_system.cc
index 70e1556..9951df6 100644
--- a/src/cobalt/storage/virtual_file_system.cc
+++ b/src/cobalt/storage/virtual_file_system.cc
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
#include "cobalt/storage/virtual_file_system.h"
#include "base/logging.h"
@@ -124,23 +123,27 @@
return bytes_written;
}
-void VirtualFileSystem::Deserialize(const uint8* buffer, int buffer_size) {
+bool VirtualFileSystem::Deserialize(const uint8* buffer, int buffer_size_in) {
+ const uint8* caller_buffer = buffer;
+
base::AutoLock lock(file_table_lock_);
ClearFileTable();
- if (buffer_size < 0) {
- DLOG(ERROR) << "Buffer size must be positive: "
- << buffer_size << " < 0.";
- return;
+ if (buffer_size_in < 0) {
+ DLOG(ERROR) << "Buffer size must be positive: " << buffer_size_in
+ << " < 0.";
+ return false;
}
+ size_t buffer_size = static_cast<size_t>(buffer_size_in);
+
// The size of the buffer must be checked before copying the beginning of it
// into a SerializedHeader.
- if (static_cast<size_t>(buffer_size) < sizeof(SerializedHeader)) {
+ if (buffer_size < sizeof(SerializedHeader)) {
DLOG(ERROR) << "Buffer size " << buffer_size
<< " is too small to contain a SerializedHeader of size "
<< sizeof(SerializedHeader) << "; operation aborted.";
- return;
+ return false;
}
// Read in expected number of files
@@ -152,23 +155,36 @@
if (buffer_version != 0) {
// Note: We would handle old versions here, if necessary.
DLOG(ERROR) << "Attempted to load a different version; operation aborted.";
- return;
- } else if (header.file_size != buffer_size) {
+ return false;
+ } else if (static_cast<size_t>(header.file_size) != buffer_size) {
DLOG(ERROR) << "Buffer size mismatch: " << header.file_size
<< " != " << buffer_size;
- return;
+ return false;
}
for (int i = 0; i < header.file_count; ++i) {
+ size_t buffer_used = static_cast<size_t>(buffer - caller_buffer);
+ if (buffer_size < buffer_used) {
+ DLOG(ERROR) << "Buffer overrun deserializing files";
+ ClearFileTable();
+ return false;
+ }
+ size_t buffer_remaining = buffer_size - buffer_used;
+
VirtualFile* file = new VirtualFile("");
- int bytes = file->Deserialize(buffer);
+
+ int bytes = file->Deserialize(buffer, buffer_remaining);
if (bytes > 0) {
buffer += bytes;
table_[file->name_] = file;
} else {
DLOG(WARNING) << "Failed to deserialize virtual file system.";
+ delete file;
+ ClearFileTable();
+ return false;
}
}
+ return true;
}
void VirtualFileSystem::ClearFileTable() {
diff --git a/src/cobalt/storage/virtual_file_system.h b/src/cobalt/storage/virtual_file_system.h
index e0b69b0..bef9a4c 100644
--- a/src/cobalt/storage/virtual_file_system.h
+++ b/src/cobalt/storage/virtual_file_system.h
@@ -59,7 +59,8 @@
int Serialize(uint8* buffer, bool dry_run);
// Deserializes a file system from a memory buffer.
- void Deserialize(const uint8* buffer, int buffer_size);
+ // Returns false on failure.
+ bool Deserialize(const uint8* buffer, int buffer_size);
// Simple file open. Will create a file if it does not exist, and files are
// always readable and writable.
diff --git a/src/cobalt/storage/virtual_file_system_test.cc b/src/cobalt/storage/virtual_file_system_test.cc
index e507516..de36f6f 100644
--- a/src/cobalt/storage/virtual_file_system_test.cc
+++ b/src/cobalt/storage/virtual_file_system_test.cc
@@ -21,6 +21,8 @@
#include "base/memory/scoped_ptr.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "starboard/client_porting/poem/string_poem.h"
+
namespace cobalt {
namespace storage {
@@ -102,6 +104,32 @@
EXPECT_EQ(4, file->Size());
}
+TEST_F(VirtualFileSystemTest, Truncate) {
+ VirtualFile* file = vfs_.Open("file1.tmp");
+ ASSERT_TRUE(file);
+ EXPECT_EQ(0, file->Size());
+
+ const char* data = "test";
+ char file_contents[4];
+ // Write a few bytes of random data.
+ file->Write(data, 4, 0);
+ EXPECT_EQ(4, file->Size());
+ int bytes = file->Read(file_contents, sizeof(file_contents), 0);
+ EXPECT_EQ(4, bytes);
+ EXPECT_EQ(0, memcmp(file_contents, data, static_cast<size_t>(bytes)));
+
+ file->Truncate(3);
+ EXPECT_EQ(3, file->Size());
+ bytes = file->Read(file_contents, sizeof(file_contents), 0);
+ EXPECT_EQ(3, bytes);
+ EXPECT_EQ(0, memcmp(file_contents, data, static_cast<size_t>(bytes)));
+
+ file->Truncate(0);
+ EXPECT_EQ(0, file->Size());
+ bytes = file->Read(file_contents, sizeof(file_contents), 0);
+ EXPECT_EQ(0, bytes);
+}
+
TEST_F(VirtualFileSystemTest, SerializeDeserialize) {
// Create a few files and write some data
VirtualFile* file = vfs_.Open("file1.tmp");
@@ -116,6 +144,58 @@
int data2_size = 4;
file->Write(data2, data2_size, 0);
+ file = vfs_.Open("file3.tmp");
+ EXPECT_TRUE(file != NULL);
+ const char data3[] = "";
+ int data3_size = 0;
+ file->Write(data3, data3_size, 0);
+
+ // First perform a dry run to figure out how much space we need.
+ int bytes = vfs_.Serialize(NULL, true /*dry run*/);
+ scoped_array<uint8> buffer(new uint8[static_cast<size_t>(bytes)]);
+
+ // Now serialize and deserialize
+ vfs_.Serialize(buffer.get(), false /*dry run*/);
+
+ // Deserialize the data into a new vfs
+ VirtualFileSystem new_vfs;
+ ASSERT_TRUE(new_vfs.Deserialize(buffer.get(), bytes));
+
+ // Make sure the new vfs contains all the expected data.
+ char file_contents[VirtualFile::kMaxVfsPathname];
+ file = new_vfs.Open("file1.tmp");
+ EXPECT_TRUE(file != NULL);
+ bytes = file->Read(file_contents, sizeof(file_contents), 0);
+ EXPECT_EQ(data1_size, bytes);
+ EXPECT_EQ(0, memcmp(file_contents, data1, static_cast<size_t>(bytes)));
+
+ file = new_vfs.Open("file2.tmp");
+ EXPECT_TRUE(file != NULL);
+ bytes = file->Read(file_contents, sizeof(file_contents), 0);
+ EXPECT_EQ(data2_size, bytes);
+ EXPECT_EQ(0, memcmp(file_contents, data2, static_cast<size_t>(bytes)));
+
+ file = new_vfs.Open("file3.tmp");
+ EXPECT_TRUE(file != NULL);
+ bytes = file->Read(file_contents, sizeof(file_contents), 0);
+ EXPECT_EQ(data3_size, bytes);
+ EXPECT_EQ(0, memcmp(file_contents, data3, static_cast<size_t>(bytes)));
+}
+
+TEST_F(VirtualFileSystemTest, DeserializeTruncated) {
+ // Create a few files and write some data
+ VirtualFile* file = vfs_.Open("file1.tmp");
+ EXPECT_TRUE(file != NULL);
+ const char data1[] = "abc";
+ int data1_size = 3;
+ file->Write(data1, data1_size, 0);
+
+ file = vfs_.Open("file2.tmp");
+ EXPECT_TRUE(file != NULL);
+ const char data2[] = "defg";
+ int data2_size = 4;
+ file->Write(data2, data2_size, 0);
+
// First perform a dry run to figure out how much space we need.
int bytes = vfs_.Serialize(NULL, true /*dry run*/);
scoped_array<uint8> buffer(new uint8[static_cast<size_t>(bytes)]);
@@ -123,23 +203,39 @@
// Now serialize and deserialize
vfs_.Serialize(buffer.get(), false /*dry run*/);
- // Deserialize the data into a new vfs
- VirtualFileSystem new_vfs;
- new_vfs.Deserialize(buffer.get(), bytes);
+ for (int i = 1; i < bytes; i++) {
+ // Corrupt the header
+ VirtualFileSystem::SerializedHeader header;
+ memcpy(&header, buffer.get(), sizeof(header));
+ header.file_size = header.file_size - i;
+ memcpy(buffer.get(), &header, sizeof(header));
- // Make sure the new vfs contains all the expected data.
- char file_contents[VirtualFile::kMaxVfsPathname];
- file = new_vfs.Open("file1.tmp");
- EXPECT_TRUE(file != NULL);
- bytes = file->Read(file_contents, sizeof(file_contents), 0);
- EXPECT_EQ(data1_size, bytes);
- EXPECT_EQ(0, memcmp(file_contents, data1, static_cast<size_t>(bytes)));
+ // Deserialize the data into a new vfs
+ VirtualFileSystem new_vfs;
+ EXPECT_FALSE(new_vfs.Deserialize(buffer.get(), bytes - i));
+ }
+}
- file = new_vfs.Open("file2.tmp");
- EXPECT_TRUE(file != NULL);
- bytes = file->Read(file_contents, sizeof(file_contents), 0);
- EXPECT_EQ(data2_size, bytes);
- EXPECT_EQ(0, memcmp(file_contents, data2, static_cast<size_t>(bytes)));
+TEST_F(VirtualFileSystemTest, DeserializeBadData) {
+ scoped_array<uint8> buffer(new uint8[0]);
+ EXPECT_EQ(false, vfs_.Deserialize(buffer.get(), 0));
+ EXPECT_EQ(false, vfs_.Deserialize(buffer.get(), -1));
+ buffer.reset(new uint8[1]);
+ EXPECT_EQ(false, vfs_.Deserialize(buffer.get(), 1));
+ buffer.reset(new uint8[sizeof(VirtualFileSystem::SerializedHeader)]);
+ VirtualFileSystem::SerializedHeader header = {};
+ header.version = -1;
+ memcpy(buffer.get(), &header, sizeof(header));
+ EXPECT_EQ(false,
+ vfs_.Deserialize(buffer.get(),
+ sizeof(VirtualFileSystem::SerializedHeader)));
+ memcpy(&(header.version), "SAV0", sizeof(header.version));
+ header.file_size = sizeof(VirtualFileSystem::SerializedHeader);
+ memcpy(buffer.get(), &header, sizeof(header));
+ EXPECT_EQ(true,
+ vfs_.Deserialize(buffer.get(),
+ sizeof(VirtualFileSystem::SerializedHeader)));
+ ASSERT_EQ(0, vfs_.ListFiles().size());
}
TEST_F(VirtualFileSystemTest, GetHeaderVersion) {
diff --git a/src/cobalt/webdriver/element_driver.cc b/src/cobalt/webdriver/element_driver.cc
index 6d05638..950581a 100644
--- a/src/cobalt/webdriver/element_driver.cc
+++ b/src/cobalt/webdriver/element_driver.cc
@@ -105,34 +105,38 @@
Keyboard::TranslateToKeyEvents(keys.utf8_keys(), Keyboard::kReleaseModifiers,
events.get());
// Dispatch the keyboard events.
- return util::CallOnMessageLoop(
+ return util::CallOnMessageLoopWithRetry(
element_message_loop_,
base::Bind(&ElementDriver::SendKeysInternal, base::Unretained(this),
- base::Passed(&events)));
+ base::Passed(&events)),
+ protocol::Response::kStaleElementReference);
}
util::CommandResult<protocol::ElementId> ElementDriver::FindElement(
const protocol::SearchStrategy& strategy) {
- return util::CallOnMessageLoop(
+ return util::CallOnMessageLoopWithRetry(
element_message_loop_,
base::Bind(&ElementDriver::FindElementsInternal<protocol::ElementId>,
- base::Unretained(this), strategy));
+ base::Unretained(this), strategy),
+ protocol::Response::kStaleElementReference);
}
util::CommandResult<std::vector<protocol::ElementId> >
ElementDriver::FindElements(const protocol::SearchStrategy& strategy) {
- return util::CallOnMessageLoop(
+ return util::CallOnMessageLoopWithRetry(
element_message_loop_,
base::Bind(&ElementDriver::FindElementsInternal<ElementIdVector>,
- base::Unretained(this), strategy));
+ base::Unretained(this), strategy),
+ protocol::Response::kNoSuchElement);
}
util::CommandResult<bool> ElementDriver::Equals(
const ElementDriver* other_element_driver) {
- return util::CallOnMessageLoop(
+ return util::CallOnMessageLoopWithRetry(
element_message_loop_,
base::Bind(&ElementDriver::EqualsInternal, base::Unretained(this),
- other_element_driver));
+ other_element_driver),
+ protocol::Response::kStaleElementReference);
}
util::CommandResult<base::optional<std::string> > ElementDriver::GetAttribute(
diff --git a/src/cobalt/webdriver/server.cc b/src/cobalt/webdriver/server.cc
index ab7b3eb..9c69788 100644
--- a/src/cobalt/webdriver/server.cc
+++ b/src/cobalt/webdriver/server.cc
@@ -176,11 +176,11 @@
};
} // namespace
-WebDriverServer::WebDriverServer(int port,
+WebDriverServer::WebDriverServer(int port, const std::string& listen_ip,
const HandleRequestCallback& callback)
: handle_request_callback_(callback) {
// Create http server
- factory_.reset(new net::TCPListenSocketFactory("0.0.0.0", port));
+ factory_.reset(new net::TCPListenSocketFactory(listen_ip, port));
server_ = new net::HttpServer(*factory_, this);
LOG(INFO) << "Starting WebDriver server on port " << port;
}
diff --git a/src/cobalt/webdriver/server.h b/src/cobalt/webdriver/server.h
index a35a947..b81008a 100644
--- a/src/cobalt/webdriver/server.h
+++ b/src/cobalt/webdriver/server.h
@@ -79,7 +79,8 @@
HttpMethod, const std::string&, scoped_ptr<base::Value>,
scoped_ptr<ResponseHandler>)> HandleRequestCallback;
- WebDriverServer(int port, const HandleRequestCallback& callback);
+ WebDriverServer(int port, const std::string& listen_ip,
+ const HandleRequestCallback& callback);
protected:
// net::HttpServer::Delegate implementation.
diff --git a/src/cobalt/webdriver/util/call_on_message_loop.h b/src/cobalt/webdriver/util/call_on_message_loop.h
index 8b28a82..f3af96f 100644
--- a/src/cobalt/webdriver/util/call_on_message_loop.h
+++ b/src/cobalt/webdriver/util/call_on_message_loop.h
@@ -35,26 +35,63 @@
CallOnMessageLoopHelper(
const scoped_refptr<base::MessageLoopProxy>& message_loop_proxy,
const CallbackType& callback)
- : completed_event_(false, false) {
+ : completed_event_(true, false), success_(false) {
DCHECK_NE(base::MessageLoopProxy::current(), message_loop_proxy);
+ scoped_ptr<DeletionSignaler> dt(new DeletionSignaler(&completed_event_));
+ // Note that while MessageLoopProxy::PostTask returns false
+ // after the message loop has gone away, it still can return true
+ // even if tasks are posted during shutdown and will never be run,
+ // so we ignore this return value.
message_loop_proxy->PostTask(
- FROM_HERE, base::Bind(&CallOnMessageLoopHelper::CallAndSignal,
- base::Unretained(this), callback));
+ FROM_HERE,
+ base::Bind(&CallOnMessageLoopHelper::Call, base::Unretained(this),
+ callback, base::Passed(&dt)));
}
- ReturnValue WaitForResult() {
+ // Waits for result, filling |out| with the return value if successful.
+ // Returns true on success or false if the message loop went away
+ // before the task was executed.
+ bool WaitForResult(ReturnValue* out) {
completed_event_.Wait();
- return result_;
+ if (success_) {
+ *out = result_;
+ }
+ return success_;
+ }
+
+ ~CallOnMessageLoopHelper() {
+ // We must ensure that we've waited for completion otherwise
+ // DeletionSignaler will have a use-after-free.
+ completed_event_.Wait();
}
private:
- void CallAndSignal(const CallbackType& callback) {
+ // DeletionSignaler signals an event when the destructor is called.
+ // This allows us to use the base::Passed mechanism to signal our
+ // completed_event_ both when Call() has been invoked and when
+ // the message loop has been deleted.
+ class DeletionSignaler {
+ public:
+ base::WaitableEvent* to_signal_;
+
+ explicit DeletionSignaler(base::WaitableEvent* to_signal)
+ : to_signal_(to_signal) {}
+
+ ~DeletionSignaler() { to_signal_->Signal(); }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DeletionSignaler);
+ };
+
+ void Call(const CallbackType& callback,
+ scoped_ptr<DeletionSignaler> dt ALLOW_UNUSED) {
result_ = callback.Run();
- completed_event_.Signal();
+ success_ = true;
}
base::WaitableEvent completed_event_;
ReturnValue result_;
+ bool success_;
};
// Used with CallWeakOnMessageLoop.
@@ -71,14 +108,41 @@
} // namespace internal
// Call the base::Callback on the specified message loop and wait for it to
-// complete, then return the result of the callback.
+// complete. Returns true if successful, or false if the underlying
+// PostTask failed. This can happen if a WebModule shuts down due to a page
+// navigation.
+//
+// On success, |out| is set to the result.
template <class ReturnValue>
-ReturnValue CallOnMessageLoop(
+bool TryCallOnMessageLoop(
const scoped_refptr<base::MessageLoopProxy>& message_loop_proxy,
- const base::Callback<ReturnValue(void)>& callback) {
+ const base::Callback<ReturnValue(void)>& callback, ReturnValue* out) {
internal::CallOnMessageLoopHelper<ReturnValue> call_helper(message_loop_proxy,
callback);
- return call_helper.WaitForResult();
+ return call_helper.WaitForResult(out);
+}
+
+// Tries to call |callback| on messageloop |message_loop_proxy|,
+// retrying a few times if the WebModule thread appears to have gone
+// away (perhaps because of navigation). If failure persists,
+// returns a CommandResult of |window_disappeared_code|.
+template <typename ReturnValue>
+util::CommandResult<ReturnValue> CallOnMessageLoopWithRetry(
+ const scoped_refptr<base::MessageLoopProxy>& message_loop_proxy,
+ const base::Callback<util::CommandResult<ReturnValue>(void)>& callback,
+ protocol::Response::StatusCode window_disappeared_code) {
+ util::CommandResult<ReturnValue> result;
+
+ const int kRetryAttempts = 5;
+ bool success = false;
+ for (int i = 0; !success && i < kRetryAttempts; i++) {
+ success = TryCallOnMessageLoop(message_loop_proxy, callback, &result);
+ }
+
+ if (!success) {
+ result = util::CommandResult<ReturnValue>(window_disappeared_code);
+ }
+ return result;
}
// Supports a common pattern in the various XXXDriver classes.
@@ -97,10 +161,11 @@
protocol::Response::StatusCode no_such_object_code) {
typedef util::CommandResult<ReturnValue> CommandResult;
typedef base::optional<ReturnValue> InternalResult;
- InternalResult result = util::CallOnMessageLoop(
+ InternalResult result;
+ bool success = util::TryCallOnMessageLoop(
message_loop,
- base::Bind(&internal::RunWeak<T, ReturnValue>, get_weak, cb));
- if (result) {
+ base::Bind(&internal::RunWeak<T, ReturnValue>, get_weak, cb), &result);
+ if (success && result) {
return CommandResult(result.value());
} else {
return CommandResult(no_such_object_code);
diff --git a/src/cobalt/webdriver/web_driver_module.cc b/src/cobalt/webdriver/web_driver_module.cc
index 3d510cb..9b4b9d0 100644
--- a/src/cobalt/webdriver/web_driver_module.cc
+++ b/src/cobalt/webdriver/web_driver_module.cc
@@ -159,8 +159,11 @@
} // namespace
+const char WebDriverModule::kDefaultListenIp[] = "0.0.0.0";
+
WebDriverModule::WebDriverModule(
- int server_port, const CreateSessionDriverCB& create_session_driver_cb,
+ int server_port, const std::string& listen_ip,
+ const CreateSessionDriverCB& create_session_driver_cb,
const GetScreenshotFunction& get_screenshot_function,
const SetProxyFunction& set_proxy_function,
const base::Closure& shutdown_cb)
@@ -405,7 +408,7 @@
base::Thread::Options(MessageLoop::TYPE_IO, 0));
webdriver_thread_.message_loop()->PostTask(
FROM_HERE, base::Bind(&WebDriverModule::StartServer,
- base::Unretained(this), server_port));
+ base::Unretained(this), server_port, listen_ip));
}
WebDriverModule::~WebDriverModule() {
@@ -428,11 +431,12 @@
}
}
-void WebDriverModule::StartServer(int server_port) {
+void WebDriverModule::StartServer(int server_port,
+ const std::string& listen_ip) {
DCHECK(thread_checker_.CalledOnValidThread());
// Create a new WebDriverServer and pass in the Dispatcher.
webdriver_server_.reset(new WebDriverServer(
- server_port,
+ server_port, listen_ip,
base::Bind(&WebDriverDispatcher::HandleWebDriverServerRequest,
base::Unretained(webdriver_dispatcher_.get()))));
}
diff --git a/src/cobalt/webdriver/web_driver_module.h b/src/cobalt/webdriver/web_driver_module.h
index 2df30dd..b0c89ed 100644
--- a/src/cobalt/webdriver/web_driver_module.h
+++ b/src/cobalt/webdriver/web_driver_module.h
@@ -48,7 +48,10 @@
typedef base::Callback<void(const ScreenshotCompleteCallback&)>
GetScreenshotFunction;
typedef base::Callback<void(const std::string&)> SetProxyFunction;
- WebDriverModule(int server_port,
+ // Use this as the default listen_ip. It means "any interface on the local
+ // machine" eg INADDR_ANY.
+ static const char kDefaultListenIp[];
+ WebDriverModule(int server_port, const std::string& listen_ip,
const CreateSessionDriverCB& create_session_driver_cb,
const GetScreenshotFunction& get_screenshot_function,
const SetProxyFunction& set_proxy_function,
@@ -65,7 +68,7 @@
void OnWindowRecreated();
private:
- void StartServer(int server_port);
+ void StartServer(int server_port, const std::string& listen_ip);
void StopServer();
void GetServerStatus(
const base::Value* parameters,
diff --git a/src/cobalt/webdriver/window_driver.cc b/src/cobalt/webdriver/window_driver.cc
index 57b0467..30c1415 100644
--- a/src/cobalt/webdriver/window_driver.cc
+++ b/src/cobalt/webdriver/window_driver.cc
@@ -136,9 +136,12 @@
// It's expected that the WebDriver thread is the only other thread to call
// this function.
DCHECK(thread_checker_.CalledOnValidThread());
- return util::CallOnMessageLoop(window_message_loop_,
- base::Bind(&WindowDriver::GetElementDriver, base::Unretained(this),
- element_id));
+ ElementDriver* result;
+ bool success = util::TryCallOnMessageLoop(
+ window_message_loop_, base::Bind(&WindowDriver::GetElementDriver,
+ base::Unretained(this), element_id),
+ &result);
+ return success ? result : NULL;
}
DCHECK_EQ(base::MessageLoopProxy::current(), window_message_loop_);
ElementDriverMap::iterator it = element_drivers_.find(element_id.id());
@@ -159,9 +162,10 @@
util::CommandResult<void> WindowDriver::Navigate(const GURL& url) {
DCHECK(thread_checker_.CalledOnValidThread());
- return util::CallOnMessageLoop(
+ return util::CallOnMessageLoopWithRetry(
window_message_loop_,
- base::Bind(&WindowDriver::NavigateInternal, base::Unretained(this), url));
+ base::Bind(&WindowDriver::NavigateInternal, base::Unretained(this), url),
+ protocol::Response::kNoSuchWindow);
}
util::CommandResult<std::string> WindowDriver::GetCurrentUrl() {
@@ -186,17 +190,21 @@
const protocol::SearchStrategy& strategy) {
DCHECK(thread_checker_.CalledOnValidThread());
- return util::CallOnMessageLoop(window_message_loop_,
+ return util::CallOnMessageLoopWithRetry(
+ window_message_loop_,
base::Bind(&WindowDriver::FindElementsInternal<protocol::ElementId>,
- base::Unretained(this), strategy));
+ base::Unretained(this), strategy),
+ protocol::Response::kNoSuchElement);
}
util::CommandResult<std::vector<protocol::ElementId> >
WindowDriver::FindElements(const protocol::SearchStrategy& strategy) {
DCHECK(thread_checker_.CalledOnValidThread());
- return util::CallOnMessageLoop(window_message_loop_,
+ return util::CallOnMessageLoopWithRetry(
+ window_message_loop_,
base::Bind(&WindowDriver::FindElementsInternal<ElementIdVector>,
- base::Unretained(this), strategy));
+ base::Unretained(this), strategy),
+ protocol::Response::kNoSuchElement);
}
util::CommandResult<std::string> WindowDriver::GetSource() {
@@ -218,10 +226,11 @@
SyncExecuteResultHandler result_handler;
- CommandResult result = util::CallOnMessageLoop(
+ CommandResult result = util::CallOnMessageLoopWithRetry(
window_message_loop_,
base::Bind(&WindowDriver::ExecuteScriptInternal, base::Unretained(this),
- script, base::nullopt, &result_handler));
+ script, base::nullopt, &result_handler),
+ protocol::Response::kNoSuchWindow);
if (result.is_success()) {
return CommandResult(protocol::ScriptResult(result_handler.result()));
} else {
@@ -240,10 +249,11 @@
const base::TimeDelta kDefaultAsyncTimeout =
base::TimeDelta::FromMilliseconds(0);
AsyncExecuteResultHandler result_handler;
- CommandResult result = util::CallOnMessageLoop(
+ CommandResult result = util::CallOnMessageLoopWithRetry(
window_message_loop_,
base::Bind(&WindowDriver::ExecuteScriptInternal, base::Unretained(this),
- script, kDefaultAsyncTimeout, &result_handler));
+ script, kDefaultAsyncTimeout, &result_handler),
+ protocol::Response::kNoSuchWindow);
if (!result.is_success()) {
return result;
@@ -264,16 +274,18 @@
Keyboard::TranslateToKeyEvents(keys.utf8_keys(), Keyboard::kKeepModifiers,
events.get());
// Dispatch the keyboard events.
- return util::CallOnMessageLoop(
+ return util::CallOnMessageLoopWithRetry(
window_message_loop_,
base::Bind(&WindowDriver::SendKeysInternal, base::Unretained(this),
- base::Passed(&events)));
+ base::Passed(&events)),
+ protocol::Response::kNoSuchWindow);
}
util::CommandResult<protocol::ElementId> WindowDriver::GetActiveElement() {
- return util::CallOnMessageLoop(
+ return util::CallOnMessageLoopWithRetry(
window_message_loop_, base::Bind(&WindowDriver::GetActiveElementInternal,
- base::Unretained(this)));
+ base::Unretained(this)),
+ protocol::Response::kNoSuchWindow);
}
util::CommandResult<void> WindowDriver::SwitchFrame(
@@ -318,9 +330,10 @@
util::CommandResult<void> WindowDriver::AddCookie(
const protocol::Cookie& cookie) {
DCHECK(thread_checker_.CalledOnValidThread());
- return util::CallOnMessageLoop(window_message_loop_,
- base::Bind(&WindowDriver::AddCookieInternal,
- base::Unretained(this), cookie));
+ return util::CallOnMessageLoopWithRetry(
+ window_message_loop_, base::Bind(&WindowDriver::AddCookieInternal,
+ base::Unretained(this), cookie),
+ protocol::Response::kNoSuchWindow);
}
protocol::ElementId WindowDriver::ElementToId(
diff --git a/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py b/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py
index cd03ee2..fbba36b 100755
--- a/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py
+++ b/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py
@@ -51,6 +51,8 @@
STARTUP_TIMEOUT_SECONDS = 2 * 60
+WEBDRIVER_HTTP_TIMEOUT_SECS = 2 * 60
+
COBALT_EXIT_TIMEOUT_SECONDS = 5
COBALT_WEBDRIVER_CAPABILITIES = {
@@ -180,6 +182,7 @@
url = "http://{}:{}/".format(self._GetIPAddress(), port)
self.webdriver = self.selenium_webdriver_module.Remote(
url, COBALT_WEBDRIVER_CAPABILITIES)
+ self.webdriver.command_executor.set_timeout(WEBDRIVER_HTTP_TIMEOUT_SECS)
print("Selenium Connected\n", file=self.log_file)
_webdriver = self.webdriver
self.test_script_started.set()
@@ -262,11 +265,10 @@
args.log_file) as runner:
unittest.main(testRunner=unittest.TextTestRunner(
verbosity=0, stream=runner.log_file))
- return 0
except TimeoutException:
print("Timeout waiting for Cobalt to start", file=sys.stderr)
- return 1
+ sys.exit(1)
if __name__ == "__main__":
- sys.exit(main())
+ main()
diff --git a/src/cobalt/webdriver_benchmarks/tests/test_median.py b/src/cobalt/webdriver_benchmarks/tests/test_median.py
deleted file mode 100755
index f265b15..0000000
--- a/src/cobalt/webdriver_benchmarks/tests/test_median.py
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/usr/bin/python2
-# Copyright 2016 Google Inc. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-# ==============================================================================
-"""Tests the functionality median function."""
-from __future__ import absolute_import
-from __future__ import division
-from __future__ import print_function
-
-import os
-import sys
-# The parent directory is a module
-sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
-
-# pylint: disable=C6204,C6203
-from tv_testcase import _median as median
-import unittest
-
-
-class MedianTest(unittest.TestCase):
-
- def test_empty_case(self):
- self.assertEqual(median([]), None)
-
- def test_one_item(self):
- self.assertEqual(median([1]), 1)
-
- def test_two_items(self):
- self.assertAlmostEqual(median([4, 1]), 2.5)
-
- def test_three_items(self):
- self.assertAlmostEqual(median([4, -4, -2]), -2)
-
- def test_four_items(self):
- self.assertAlmostEqual(median([4, 0, 1, -2]), 0.5)
-
-
-if __name__ == '__main__':
- sys.exit(unittest.main())
diff --git a/src/content/browser/speech/chunked_byte_buffer.cc b/src/content/browser/speech/chunked_byte_buffer.cc
index a43e40a..8ac9c92 100644
--- a/src/content/browser/speech/chunked_byte_buffer.cc
+++ b/src/content/browser/speech/chunked_byte_buffer.cc
@@ -7,15 +7,15 @@
#include <algorithm>
#include <utility>
-#include "base/lazy_instance.h"
+#include "base/basictypes.h"
#include "base/logging.h"
namespace {
static const size_t kHeaderLength = sizeof(uint32_t);
-static_assert(sizeof(size_t) >= kHeaderLength,
- "chunked byte buffer not supported on this architecture");
+COMPILE_ASSERT(sizeof(size_t) >= kHeaderLength,
+ chunked_byte_buffer_not_supported_on_this_architecture);
uint32_t ReadBigEndian32(const uint8_t* buffer) {
return (static_cast<uint32_t>(buffer[3])) |
@@ -103,16 +103,16 @@
return !chunks_.empty();
}
-std::unique_ptr<std::vector<uint8_t>> ChunkedByteBuffer::PopChunk() {
+scoped_ptr<std::vector<uint8_t> > ChunkedByteBuffer::PopChunk() {
if (chunks_.empty())
- return std::unique_ptr<std::vector<uint8_t>>();
- std::unique_ptr<Chunk> chunk(*chunks_.begin());
+ return scoped_ptr<std::vector<uint8_t> >().Pass();
+ scoped_ptr<Chunk> chunk(*chunks_.begin());
chunks_.weak_erase(chunks_.begin());
DCHECK_EQ(chunk->header.size(), kHeaderLength);
DCHECK_EQ(chunk->content->size(), chunk->ExpectedContentLength());
total_bytes_stored_ -= chunk->content->size();
total_bytes_stored_ -= kHeaderLength;
- return std::move(chunk->content);
+ return chunk->content.Pass();
}
void ChunkedByteBuffer::Clear() {
diff --git a/src/content/browser/speech/chunked_byte_buffer.h b/src/content/browser/speech/chunked_byte_buffer.h
index 41e273d..483ddb9 100644
--- a/src/content/browser/speech/chunked_byte_buffer.h
+++ b/src/content/browser/speech/chunked_byte_buffer.h
@@ -5,16 +5,12 @@
#ifndef CONTENT_BROWSER_SPEECH_CHUNKED_BYTE_BUFFER_H_
#define CONTENT_BROWSER_SPEECH_CHUNKED_BYTE_BUFFER_H_
-#include <stddef.h>
-#include <stdint.h>
-
#include <memory>
#include <string>
#include <vector>
-#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
-#include "content/common/content_export.h"
namespace content {
@@ -28,7 +24,7 @@
//
// E.g. 00 00 00 04 xx xx xx xx 00 00 00 02 yy yy 00 00 00 04 zz zz zz zz
// [----- CHUNK 1 -------] [--- CHUNK 2 ---] [------ CHUNK 3 ------]
-class CONTENT_EXPORT ChunkedByteBuffer {
+class ChunkedByteBuffer {
public:
ChunkedByteBuffer();
~ChunkedByteBuffer();
@@ -44,7 +40,7 @@
// If enough data is available, reads and removes the first complete chunk
// from the buffer. Returns a NULL pointer if no complete chunk is available.
- std::unique_ptr<std::vector<uint8_t>> PopChunk();
+ scoped_ptr<std::vector<uint8_t> > PopChunk();
// Clears all the content of the buffer.
void Clear();
@@ -58,7 +54,7 @@
~Chunk();
std::vector<uint8_t> header;
- std::unique_ptr<std::vector<uint8_t>> content;
+ scoped_ptr<std::vector<uint8_t> > content;
size_t ExpectedContentLength() const;
private:
@@ -66,7 +62,7 @@
};
ScopedVector<Chunk> chunks_;
- std::unique_ptr<Chunk> partial_chunk_;
+ scoped_ptr<Chunk> partial_chunk_;
size_t total_bytes_stored_;
DISALLOW_COPY_AND_ASSIGN(ChunkedByteBuffer);
diff --git a/src/content/browser/speech/chunked_byte_buffer_unittest.cc b/src/content/browser/speech/chunked_byte_buffer_unittest.cc
index d8a5cb2..9b7be0f 100644
--- a/src/content/browser/speech/chunked_byte_buffer_unittest.cc
+++ b/src/content/browser/speech/chunked_byte_buffer_unittest.cc
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <stdint.h>
-
#include <string>
#include <vector>
@@ -42,8 +40,8 @@
EXPECT_TRUE(buffer.HasChunks());
// Remove and check chunk 1.
- std::unique_ptr<ByteVector> chunk;
- chunk = buffer.PopChunk();
+ scoped_ptr<ByteVector> chunk;
+ chunk = buffer.PopChunk().Pass();
EXPECT_TRUE(chunk != NULL);
EXPECT_EQ(4U, chunk->size());
EXPECT_EQ(0, std::char_traits<uint8_t>::compare(kChunks + 4, &(*chunk)[0],
@@ -52,7 +50,7 @@
EXPECT_TRUE(buffer.HasChunks());
// Read and check chunk 2.
- chunk = buffer.PopChunk();
+ chunk = buffer.PopChunk().Pass();
EXPECT_TRUE(chunk != NULL);
EXPECT_EQ(2U, chunk->size());
EXPECT_EQ(0, std::char_traits<uint8_t>::compare(kChunks + 12, &(*chunk)[0],
@@ -65,7 +63,7 @@
EXPECT_EQ(5U, buffer.GetTotalLength());
// Remove and check chunk 3.
- chunk = buffer.PopChunk();
+ chunk = buffer.PopChunk().Pass();
EXPECT_TRUE(chunk != NULL);
EXPECT_EQ(1U, chunk->size());
EXPECT_EQ((*chunk)[0], kChunks[18]);
diff --git a/src/content/browser/speech/endpointer/endpointer.cc b/src/content/browser/speech/endpointer/endpointer.cc
index 1758970..e6d9941 100644
--- a/src/content/browser/speech/endpointer/endpointer.cc
+++ b/src/content/browser/speech/endpointer/endpointer.cc
@@ -4,13 +4,15 @@
#include "content/browser/speech/endpointer/endpointer.h"
-#include "base/time/time.h"
-#include "content/browser/speech/audio_buffer.h"
+#include "base/time.h"
using base::Time;
namespace {
-const int kFrameRate = 50; // 1 frame = 20ms of audio.
+// Only send |kFrameSize| of audio data to energy endpointer each time.
+// It should be smaller than the samples of audio bus which is passed in
+// |ProcessAudio|.
+const int kFrameSize = 160;
}
namespace content {
@@ -20,11 +22,9 @@
speech_input_complete_silence_length_us_(-1),
audio_frame_time_us_(0),
sample_rate_(sample_rate),
- frame_size_(0) {
+ frame_rate_(sample_rate / kFrameSize) {
Reset();
- frame_size_ = static_cast<int>(sample_rate / static_cast<float>(kFrameRate));
-
speech_input_minimum_length_us_ =
static_cast<int64_t>(1.7 * Time::kMicrosecondsPerSecond);
speech_input_complete_silence_length_us_ =
@@ -36,8 +36,8 @@
// Set the default configuration for Push To Talk mode.
EnergyEndpointerParams ep_config;
- ep_config.set_frame_period(1.0f / static_cast<float>(kFrameRate));
- ep_config.set_frame_duration(1.0f / static_cast<float>(kFrameRate));
+ ep_config.set_frame_period(1.0f / static_cast<float>(frame_rate_));
+ ep_config.set_frame_duration(1.0f / static_cast<float>(frame_rate_));
ep_config.set_endpoint_margin(0.2f);
ep_config.set_onset_window(0.15f);
ep_config.set_speech_on_window(0.4f);
@@ -62,7 +62,7 @@
waiting_for_speech_complete_timeout_ = false;
speech_previously_detected_ = false;
speech_input_complete_ = false;
- audio_frame_time_us_ = 0; // Reset time for packets sent to endpointer.
+ audio_frame_time_us_ = 0; // Reset time for packets sent to endpointer.
speech_end_time_us_ = -1;
speech_start_time_us_ = -1;
}
@@ -89,24 +89,42 @@
return energy_endpointer_.Status(time);
}
-EpStatus Endpointer::ProcessAudio(const AudioChunk& raw_audio, float* rms_out) {
- const int16_t* audio_data = raw_audio.SamplesData16();
- const int num_samples = raw_audio.NumSamples();
+EpStatus Endpointer::ProcessAudio(
+ const ShellAudioBus& audio_bus, float* rms_out) {
+ DCHECK_EQ(audio_bus.channels(), 1);
+
+ const size_t num_samples = audio_bus.frames();
+ const int16_t* audio_data = NULL;
+
+ ShellAudioBus int16_audio_bus(1, num_samples, ShellAudioBus::kInt16,
+ ShellAudioBus::kInterleaved);
+
+ if (audio_bus.sample_type() == ShellAudioBus::kFloat32) {
+ int16_audio_bus.Assign(audio_bus);
+ DCHECK_EQ(int16_audio_bus.sample_type(), ShellAudioBus::kInt16);
+ audio_data =
+ reinterpret_cast<const int16_t*>(int16_audio_bus.interleaved_data());
+ } else {
+ DCHECK_EQ(audio_bus.sample_type(), ShellAudioBus::kInt16);
+ audio_data =
+ reinterpret_cast<const int16_t*>(audio_bus.interleaved_data());
+ }
+
EpStatus ep_status = EP_PRE_SPEECH;
- // Process the input data in blocks of frame_size_, dropping any incomplete
+ // Process the input data in blocks of kFrameSize, dropping any incomplete
// frames at the end (which is ok since typically the caller will be recording
// audio in multiples of our frame size).
int sample_index = 0;
- while (sample_index + frame_size_ <= num_samples) {
+ while (static_cast<size_t>(sample_index + kFrameSize) <= num_samples) {
// Have the endpointer process the frame.
energy_endpointer_.ProcessAudioFrame(audio_frame_time_us_,
audio_data + sample_index,
- frame_size_,
+ kFrameSize,
rms_out);
- sample_index += frame_size_;
- audio_frame_time_us_ += (frame_size_ * Time::kMicrosecondsPerSecond) /
- sample_rate_;
+ sample_index += kFrameSize;
+ audio_frame_time_us_ +=
+ (kFrameSize * Time::kMicrosecondsPerSecond) / sample_rate_;
// Get the status of the endpointer.
int64_t ep_time;
diff --git a/src/content/browser/speech/endpointer/endpointer.h b/src/content/browser/speech/endpointer/endpointer.h
index 5790672..986ed0a 100644
--- a/src/content/browser/speech/endpointer/endpointer.h
+++ b/src/content/browser/speech/endpointer/endpointer.h
@@ -8,14 +8,12 @@
#include <stdint.h>
#include "content/browser/speech/endpointer/energy_endpointer.h"
-#include "content/common/content_export.h"
+#include "media/base/shell_audio_bus.h"
class EpStatus;
namespace content {
-class AudioChunk;
-
// A simple interface to the underlying energy-endpointer implementation, this
// class lets callers provide audio as being recorded and let them poll to find
// when the user has stopped speaking.
@@ -44,8 +42,10 @@
// The timeout length is speech_input_complete_silence_length until
// long_speech_length, when it changes to
// long_speech_input_complete_silence_length.
-class CONTENT_EXPORT Endpointer {
+class Endpointer {
public:
+ typedef ::media::ShellAudioBus ShellAudioBus;
+
explicit Endpointer(int sample_rate);
// Start the endpointer. This should be called at the beginning of a session.
@@ -64,7 +64,7 @@
// Process a segment of audio, which may be more than one frame.
// The status of the last frame will be returned.
- EpStatus ProcessAudio(const AudioChunk& raw_audio, float* rms_out);
+ EpStatus ProcessAudio(const ShellAudioBus& audio_bus, float* rms_out);
// Get the status of the endpointer.
EpStatus Status(int64_t* time_us);
@@ -99,6 +99,8 @@
return speech_input_complete_;
}
+ int sample_rate() const { return sample_rate_; }
+
// RMS background noise level in dB.
float NoiseLevelDb() const { return energy_endpointer_.GetNoiseLevelDb(); }
@@ -146,7 +148,8 @@
bool speech_input_complete_;
EnergyEndpointer energy_endpointer_;
int sample_rate_;
- int32_t frame_size_;
+ // 1 frame = (1 / frame_rate_) second of audio.
+ int frame_rate_;
};
} // namespace content
diff --git a/src/content/browser/speech/endpointer/endpointer_unittest.cc b/src/content/browser/speech/endpointer/endpointer_unittest.cc
index 53ec4d1..929b8ad 100644
--- a/src/content/browser/speech/endpointer/endpointer_unittest.cc
+++ b/src/content/browser/speech/endpointer/endpointer_unittest.cc
@@ -4,8 +4,8 @@
#include <stdint.h>
-#include "content/browser/speech/audio_buffer.h"
#include "content/browser/speech/endpointer/endpointer.h"
+#include "media/base/shell_audio_bus.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
@@ -15,8 +15,13 @@
// At 8 sample per second a 20 ms frame is 160 samples, which corrsponds
// to the AMR codec.
const int kFrameSize = kSampleRate / kFrameRate; // 160 samples.
-static_assert(kFrameSize == 160, "invalid frame size");
-}
+
+#if defined(OS_STARBOARD)
+SB_COMPILE_ASSERT(kFrameSize == 160, invalid_frame_size);
+#else
+COMPILE_ASSERT(kFrameSize == 160, invalid_frame_size);
+#endif // defined(OS_STARBOARD)
+} // namespace
namespace content {
@@ -79,7 +84,7 @@
EpStatus ProcessFrame(int64_t time,
int16_t* samples,
- int frame_size) override {
+ int /*frame_size*/) override {
endpointer_->ProcessAudioFrame(time, samples, kFrameSize, NULL);
int64_t ep_time;
return endpointer_->Status(&ep_time);
@@ -119,15 +124,15 @@
// Test endpointer wrapper class.
class EndpointerFrameProcessor : public FrameProcessor {
public:
+ typedef ::media::ShellAudioBus ShellAudioBus;
explicit EndpointerFrameProcessor(Endpointer* endpointer)
: endpointer_(endpointer) {}
- EpStatus ProcessFrame(int64_t time,
+ EpStatus ProcessFrame(int64_t /*time*/,
int16_t* samples,
- int frame_size) override {
- scoped_refptr<AudioChunk> frame(
- new AudioChunk(reinterpret_cast<uint8_t*>(samples), kFrameSize * 2, 2));
- endpointer_->ProcessAudio(*frame.get(), NULL);
+ int /*frame_size*/) override {
+ scoped_ptr<ShellAudioBus> frame(new ShellAudioBus(1, kFrameSize, samples));
+ endpointer_->ProcessAudio(*frame, NULL);
int64_t ep_time;
return endpointer_->Status(&ep_time);
}
@@ -137,8 +142,6 @@
};
TEST(EndpointerTest, TestEmbeddedEndpointerEvents) {
- const int kSampleRate = 8000; // 8 k samples per second for AMR encoding.
-
Endpointer endpointer(kSampleRate);
const int64_t kMillisecondsPerMicrosecond = 1000;
const int64_t short_timeout = 300 * kMillisecondsPerMicrosecond;
diff --git a/src/content/browser/speech/endpointer/energy_endpointer.cc b/src/content/browser/speech/endpointer/energy_endpointer.cc
index fc1d871..b8a01d5 100644
--- a/src/content/browser/speech/endpointer/energy_endpointer.cc
+++ b/src/content/browser/speech/endpointer/energy_endpointer.cc
@@ -12,7 +12,6 @@
#include <stddef.h>
#include "base/logging.h"
-#include "base/macros.h"
namespace {
@@ -37,7 +36,7 @@
float GetDecibel(float value) {
if (value > 1.0e-100)
- return 20 * log10(value);
+ return static_cast<float>(20 * log10(value));
return -2000.0;
}
@@ -81,20 +80,21 @@
insertion_index_ = 0;
decision_points_.clear();
DecisionPoint init = { -1, initial_state };
- decision_points_.resize(size, init);
+ decision_points_.resize(static_cast<size_t>(size), init);
}
void EnergyEndpointer::HistoryRing::Insert(int64_t time_us, bool decision) {
- decision_points_[insertion_index_].time_us = time_us;
- decision_points_[insertion_index_].decision = decision;
- insertion_index_ = (insertion_index_ + 1) % decision_points_.size();
+ decision_points_[static_cast<size_t>(insertion_index_)].time_us = time_us;
+ decision_points_[static_cast<size_t>(insertion_index_)].decision = decision;
+ insertion_index_ =
+ static_cast<int>((insertion_index_ + 1) % decision_points_.size());
}
int64_t EnergyEndpointer::HistoryRing::EndTime() const {
int ind = insertion_index_ - 1;
if (ind < 0)
- ind = decision_points_.size() - 1;
- return decision_points_[ind].time_us;
+ ind = static_cast<int>(decision_points_.size() - 1);
+ return decision_points_[static_cast<size_t>(ind)].time_us;
}
float EnergyEndpointer::HistoryRing::RingSum(float duration_sec) {
@@ -104,23 +104,23 @@
int64_t sum_us = 0;
int ind = insertion_index_ - 1;
if (ind < 0)
- ind = decision_points_.size() - 1;
- int64_t end_us = decision_points_[ind].time_us;
- bool is_on = decision_points_[ind].decision;
+ ind = static_cast<int>(decision_points_.size() - 1);
+ int64_t end_us = decision_points_[static_cast<size_t>(ind)].time_us;
+ bool is_on = decision_points_[static_cast<size_t>(ind)].decision;
int64_t start_us =
end_us - static_cast<int64_t>(0.5 + (1.0e6 * duration_sec));
if (start_us < 0)
start_us = 0;
size_t n_summed = 1; // n points ==> (n-1) intervals
- while ((decision_points_[ind].time_us > start_us) &&
+ while ((decision_points_[static_cast<size_t>(ind)].time_us > start_us) &&
(n_summed < decision_points_.size())) {
--ind;
if (ind < 0)
- ind = decision_points_.size() - 1;
+ ind = static_cast<int>(decision_points_.size() - 1);
if (is_on)
- sum_us += end_us - decision_points_[ind].time_us;
- is_on = decision_points_[ind].decision;
- end_us = decision_points_[ind].time_us;
+ sum_us += end_us - decision_points_[static_cast<size_t>(ind)].time_us;
+ is_on = decision_points_[static_cast<size_t>(ind)].decision;
+ end_us = decision_points_[static_cast<size_t>(ind)].time_us;
n_summed++;
}
@@ -298,6 +298,8 @@
}
break;
+ case EP_POST_SPEECH:
+ // fall-through
default:
LOG(WARNING) << "Invalid case in switch: " << status_;
break;
@@ -364,7 +366,7 @@
noise_level_ = (0.95f * noise_level_) + (0.05f * rms);
}
if (estimating_environment_ || (frame_counter_ < fast_update_frames_)) {
- decision_threshold_ = noise_level_ * 2; // 6dB above noise level.
+ decision_threshold_ = noise_level_ * 2; // 6dB above noise level.
// Set a floor
if (decision_threshold_ < params_.min_decision_threshold())
decision_threshold_ = params_.min_decision_threshold();
diff --git a/src/content/browser/speech/endpointer/energy_endpointer.h b/src/content/browser/speech/endpointer/energy_endpointer.h
index 7b0b292..2d379b9 100644
--- a/src/content/browser/speech/endpointer/energy_endpointer.h
+++ b/src/content/browser/speech/endpointer/energy_endpointer.h
@@ -42,9 +42,8 @@
#include <memory>
#include <vector>
-#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
#include "content/browser/speech/endpointer/energy_endpointer_params.h"
-#include "content/common/content_export.h"
namespace content {
@@ -57,7 +56,7 @@
EP_POST_SPEECH,
};
-class CONTENT_EXPORT EnergyEndpointer {
+class EnergyEndpointer {
public:
// The default construction MUST be followed by Init(), before any
// other use can be made of the instance.
@@ -125,7 +124,7 @@
float sample_rate_; // Sampling rate.
// Ring buffers to hold the speech activity history.
- std::unique_ptr<HistoryRing> history_;
+ scoped_ptr<HistoryRing> history_;
// Configuration parameters.
EnergyEndpointerParams params_;
diff --git a/src/content/browser/speech/endpointer/energy_endpointer_params.h b/src/content/browser/speech/endpointer/energy_endpointer_params.h
index 1510435..303b3b0 100644
--- a/src/content/browser/speech/endpointer/energy_endpointer_params.h
+++ b/src/content/browser/speech/endpointer/energy_endpointer_params.h
@@ -5,12 +5,10 @@
#ifndef CONTENT_BROWSER_SPEECH_ENDPOINTER_ENERGY_ENDPOINTER_PARAMS_H_
#define CONTENT_BROWSER_SPEECH_ENDPOINTER_ENERGY_ENDPOINTER_PARAMS_H_
-#include "content/common/content_export.h"
-
namespace content {
// Input parameters for the EnergyEndpointer class.
-class CONTENT_EXPORT EnergyEndpointerParams {
+class EnergyEndpointerParams {
public:
EnergyEndpointerParams();
diff --git a/src/content/browser/speech/speech.gyp b/src/content/browser/speech/speech.gyp
new file mode 100644
index 0000000..2db9fdf
--- /dev/null
+++ b/src/content/browser/speech/speech.gyp
@@ -0,0 +1,60 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'speech',
+ 'type': 'static_library',
+ 'sources': [
+ 'chunked_byte_buffer.cc',
+ 'chunked_byte_buffer.h',
+ 'endpointer/endpointer.cc',
+ 'endpointer/endpointer.h',
+ 'endpointer/energy_endpointer.cc',
+ 'endpointer/energy_endpointer.h',
+ 'endpointer/energy_endpointer_params.cc',
+ 'endpointer/energy_endpointer_params.h',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/media/media.gyp:media',
+ ],
+ },
+ {
+ 'target_name': 'speech_test',
+ 'type': '<(gtest_target_type)',
+ 'sources': [
+ 'chunked_byte_buffer_unittest.cc',
+ 'endpointer/endpointer_unittest.cc',
+ ],
+ 'dependencies': [
+ 'speech',
+ '<(DEPTH)/base/base.gyp:run_all_unittests',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ ],
+ },
+
+ {
+ 'target_name': 'speech_test_deploy',
+ 'type': 'none',
+ 'dependencies': [
+ 'speech_test',
+ ],
+ 'variables': {
+ 'executable_name': 'speech_test',
+ },
+ 'includes': [ '../../../starboard/build/deploy.gypi' ],
+ },
+ ],
+}
diff --git a/src/media/media.gyp b/src/media/media.gyp
index 12ebe12..4f6d3d0 100644
--- a/src/media/media.gyp
+++ b/src/media/media.gyp
@@ -431,6 +431,14 @@
'dependencies': [
'../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
'shared_memory_support',
+ ],
+ }],
+ ['OS != "ios" and cobalt != 1', {
+ 'dependencies': [
+ # This is only used in [x11|gl|skcanvas]_video_renderer.cc, but not
+ # when compiling Cobalt. When linking as a shared library, the
+ # unused optimized YUV functions are left as undefined symbols
+ # after they get dead-stripped.
'yuv_convert',
],
}],
diff --git a/src/nb/analytics/memory_tracker.cc b/src/nb/analytics/memory_tracker.cc
index cf6c868..8cb7664 100644
--- a/src/nb/analytics/memory_tracker.cc
+++ b/src/nb/analytics/memory_tracker.cc
@@ -17,6 +17,7 @@
#include "nb/analytics/memory_tracker.h"
#include "nb/analytics/memory_tracker_impl.h"
+#include "nb/scoped_ptr.h"
#include "starboard/once.h"
namespace nb {
@@ -26,7 +27,14 @@
} // namespace
MemoryTracker* MemoryTracker::Get() {
- return GetMemoryTrackerImplSingleton();
+ MemoryTracker* t = GetMemoryTrackerImplSingleton();
+ return t;
+}
+
+nb::scoped_ptr<MemoryTrackerPrintThread>
+CreateDebugPrintThread(MemoryTracker* memory_tracker) {
+ return nb::scoped_ptr<MemoryTrackerPrintThread>(
+ new MemoryTrackerPrintThread(memory_tracker));
}
} // namespace analytics
diff --git a/src/nb/analytics/memory_tracker.h b/src/nb/analytics/memory_tracker.h
index 27ef75f..4884c89 100644
--- a/src/nb/analytics/memory_tracker.h
+++ b/src/nb/analytics/memory_tracker.h
@@ -20,11 +20,14 @@
#include <vector>
#include "starboard/configuration.h"
#include "starboard/types.h"
+#include "nb/scoped_ptr.h"
namespace nb {
+
namespace analytics {
class MemoryTracker;
+class MemoryTrackerPrintThread;
class AllocationVisitor;
class AllocationGroup;
class AllocationRecord;
@@ -120,7 +123,8 @@
// Contains an allocation record for a pointer including it's size and what
// AllocationGroup it was constructed under.
-struct AllocationRecord {
+class AllocationRecord {
+ public:
AllocationRecord() : size(0), allocation_group(NULL) {}
AllocationRecord(size_t sz, AllocationGroup* group)
: size(sz), allocation_group(group) {}
@@ -131,6 +135,13 @@
AllocationGroup* allocation_group;
};
+// Creates a SimpleThread that will output the state of the memory
+// periodically. Start()/Cancel()/Join() are called AUTOMATICALLY with
+// this object. Start() is on the returned thread before it is returned.
+// Join() is automatically called on destruction.
+scoped_ptr<MemoryTrackerPrintThread>
+ CreateDebugPrintThread(MemoryTracker* memory_tracker);
+
} // namespace analytics
} // namespace nb
diff --git a/src/nb/analytics/memory_tracker_helpers.cc b/src/nb/analytics/memory_tracker_helpers.cc
index 2f4776a..a1eedc8 100644
--- a/src/nb/analytics/memory_tracker_helpers.cc
+++ b/src/nb/analytics/memory_tracker_helpers.cc
@@ -27,7 +27,8 @@
namespace analytics {
AllocationGroup::AllocationGroup(const std::string& name)
- : name_(name), allocation_bytes_(0), num_allocations_(0) {}
+ : name_(name), allocation_bytes_(0), num_allocations_(0) {
+}
AllocationGroup::~AllocationGroup() {}
@@ -35,7 +36,6 @@
if (num_bytes == 0)
return;
int num_alloc_diff = num_bytes > 0 ? 1 : -1;
-
allocation_bytes_.fetch_add(num_bytes);
num_allocations_.fetch_add(num_alloc_diff);
}
@@ -276,36 +276,5 @@
return pointer_map_array_[ToIndex(ptr)];
}
-SimpleThread::SimpleThread(const std::string& name)
- : thread_(kSbThreadInvalid), name_(name) {}
-
-SimpleThread::~SimpleThread() {}
-
-void SimpleThread::Start() {
- SbThreadEntryPoint entry_point = ThreadEntryPoint;
-
- thread_ = SbThreadCreate(0, // default stack_size.
- kSbThreadNoPriority, // default priority.
- kSbThreadNoAffinity, // default affinity.
- true, // joinable.
- name_.c_str(), entry_point, this);
-
- // SbThreadCreate() above produced an invalid thread handle.
- SB_DCHECK(thread_ != kSbThreadInvalid);
- return;
-}
-
-void* SimpleThread::ThreadEntryPoint(void* context) {
- SimpleThread* this_ptr = static_cast<SimpleThread*>(context);
- this_ptr->Run();
- return NULL;
-}
-
-void SimpleThread::DoJoin() {
- if (!SbThreadJoin(thread_, NULL)) {
- SB_DCHECK(false) << "Could not join thread.";
- }
-}
-
} // namespace analytics
} // namespace nb
diff --git a/src/nb/analytics/memory_tracker_helpers.h b/src/nb/analytics/memory_tracker_helpers.h
index d07651c..d004620 100644
--- a/src/nb/analytics/memory_tracker_helpers.h
+++ b/src/nb/analytics/memory_tracker_helpers.h
@@ -22,6 +22,7 @@
#include "nb/analytics/memory_tracker.h"
#include "nb/atomic.h"
+#include "nb/simple_thread.h"
#include "starboard/mutex.h"
#include "starboard/thread.h"
#include "starboard/types.h"
@@ -31,7 +32,7 @@
namespace analytics {
class AllocationGroup;
-struct AllocationRecord;
+class AllocationRecord;
template <typename Type>
class ThreadLocalPointer {
@@ -89,7 +90,7 @@
// together, such as "Javascript" or "Graphics".
class AllocationGroup {
public:
- AllocationGroup(const std::string& name);
+ explicit AllocationGroup(const std::string& name);
~AllocationGroup();
const std::string& name() const { return name_; }
@@ -103,6 +104,8 @@
const std::string name_;
nb::atomic_int64_t allocation_bytes_;
nb::atomic_int32_t num_allocations_;
+
+ SB_DISALLOW_COPY_AND_ASSIGN(AllocationGroup);
};
// A self locking data structure that maps strings -> AllocationGroups. This is
@@ -124,6 +127,8 @@
Map group_map_;
AllocationGroup* unaccounted_group_;
mutable starboard::Mutex mutex_;
+
+ SB_DISALLOW_COPY_AND_ASSIGN(AtomicStringAllocationGroupMap);
};
class AllocationGroupStack {
@@ -222,42 +227,6 @@
AtomicAllocationMap pointer_map_array_[kNumElements];
};
-class SimpleThread {
- public:
- explicit SimpleThread(const std::string& name);
- virtual ~SimpleThread() = 0;
-
- // Subclasses should override the Run method.
- virtual void Run() = 0;
-
- void Join() {
- Cancel();
- DoJoin();
- }
-
- // If Join() is intended to interrupt the Run() function then override
- // Cancel() to send a signal.
- // Example:
- // virtual void Cancel() { finished_ = true; }
- // virtual void Run() {
- // while (!finished_) { /* do work */ }
- // }
- virtual void Cancel() {}
-
- // Calls SbThreadCreate() and starts running code.
- void Start();
-
- private:
- static void* ThreadEntryPoint(void* context);
- void DoJoin();
- void DoStart();
-
- const std::string name_;
- SbThread thread_;
-
- SB_DISALLOW_COPY_AND_ASSIGN(SimpleThread);
-};
-
} // namespace analytics
} // namespace nb
diff --git a/src/nb/analytics/memory_tracker_impl.cc b/src/nb/analytics/memory_tracker_impl.cc
index fecda24..3a9bdf4 100644
--- a/src/nb/analytics/memory_tracker_impl.cc
+++ b/src/nb/analytics/memory_tracker_impl.cc
@@ -20,6 +20,7 @@
#include <sstream>
#include "nb/atomic.h"
+#include "starboard/log.h"
#include "starboard/atomic.h"
namespace nb {
@@ -196,25 +197,25 @@
// We might do something more interesting with UnMapMemory calls later.
OnDealloc(context, memory);
}
-
+
void MemoryTrackerImpl::OnPushAllocationGroup(
void* context,
NbMemoryScopeInfo* memory_scope_info) {
MemoryTrackerImpl* t = static_cast<MemoryTrackerImpl*>(context);
- uintptr_t* cached_handle = &(memory_scope_info->cached_handle_);
+ void** cached_handle = &(memory_scope_info->cached_handle_);
const bool allows_caching = memory_scope_info->allows_caching_;
const char* group_name = memory_scope_info->memory_scope_name_;
AllocationGroup* group = NULL;
- if (allows_caching && *cached_handle != 0) {
- group = reinterpret_cast<AllocationGroup*>(cached_handle);
+ if (allows_caching && *cached_handle != NULL) {
+ group = static_cast<AllocationGroup*>(*cached_handle);
} else {
group = t->GetAllocationGroup(group_name);
if (allows_caching) {
// Flush all pending writes so that the the pointee is well formed
// by the time the pointer becomes visible to other threads.
SbAtomicMemoryBarrier();
- *cached_handle = reinterpret_cast<uintptr_t>(group);
+ *cached_handle = static_cast<void*>(group);
}
}
@@ -269,118 +270,6 @@
owner_->SetMemoryDeletionEnabled(prev_state_);
}
-// TODO: Get rid of the nb::SimpleThread
-class MemoryTrackerPrintThread : public SimpleThread {
- public:
- MemoryTrackerPrintThread(MemoryTracker* owner)
- : SimpleThread("MemoryTrackerPrintThread"),
- finished_(false),
- owner_(owner) {}
-
- // Overridden so that the thread can exit gracefully.
- virtual void Cancel() SB_OVERRIDE { finished_.store(true); }
-
- virtual void Run() {
- struct NoMemTracking {
- NoMemTracking(MemoryTracker* owner) : owner_(owner) {
- prev_val_ = owner_->IsMemoryTrackingEnabled();
- owner_->SetMemoryTrackingEnabled(false);
- }
- ~NoMemTracking() { owner_->SetMemoryTrackingEnabled(prev_val_); }
-
- bool prev_val_;
- MemoryTracker* owner_;
- };
-
- while (!finished_.load()) {
- NoMemTracking no_mem_tracking_in_this_scope(owner_);
-
- // std::map<std::string, const AllocationGroup*> output;
- // typedef std::map<std::string, const AllocationGroup*>::const_iterator
- // MapIt;
- std::vector<const AllocationGroup*> vector_output;
- owner_->GetAllocationGroups(&vector_output);
-
- typedef std::map<std::string, const AllocationGroup*> Map;
- typedef Map::const_iterator MapIt;
-
- Map output;
- for (int i = 0; i < vector_output.size(); ++i) {
- const AllocationGroup* group = vector_output[i];
- output[group->name()] = group;
- }
-
- int32_t num_allocs = 0;
- int64_t total_bytes = 0;
-
- struct F {
- static void PrintRow(std::stringstream& ss,
- const std::string& v1,
- const std::string& v2,
- const std::string& v3) {
- ss.width(20);
- ss << std::left << v1;
- ss.width(13);
- ss << std::right << v2 << " ";
- ss.width(7);
- ss << std::right << v3 << "\n";
- }
- };
-
- if (owner_->IsMemoryTrackingEnabled()) {
- // If this isn't true then it would cause an infinite loop. The
- // following will likely crash.
- SB_DCHECK(false) << "Unexpected, memory tracking should be disabled.";
- }
-
- std::stringstream ss;
- for (MapIt it = output.begin(); it != output.end(); ++it) {
- const AllocationGroup* group = it->second;
- if (!group) {
- continue;
- }
-
- int32_t num_group_allocs = -1;
- int64_t total_group_bytes = -1;
-
- group->GetAggregateStats(&num_group_allocs, &total_group_bytes);
- SB_DCHECK(-1 != num_group_allocs);
- SB_DCHECK(-1 != total_group_bytes);
- num_allocs += num_group_allocs;
- total_bytes += total_group_bytes;
-
- ss.width(20);
- ss << std::left << it->first;
- ss.width(13);
- ss << std::right << NumberFormatWithCommas(total_group_bytes) << " ";
- ss.width(7);
- ss << std::right << NumberFormatWithCommas(num_group_allocs) << "\n";
- }
- ss << "-------------------------------\n";
-
- SB_LOG(INFO) << "\n"
- << "Total Bytes Allocated: "
- << NumberFormatWithCommas(total_bytes) << "\n"
- << "Total allocations: "
- << NumberFormatWithCommas(num_allocs) << "\n\n" << ss.str();
-
- SbThreadSleep(250);
- }
- }
-
- private:
- atomic_bool finished_;
- MemoryTracker* owner_;
-};
-
-void MemoryTrackerImpl::Debug_EnablePrintOutThread() {
- if (debug_output_thread_) {
- return;
- } // Already enabled.
- debug_output_thread_.reset(new MemoryTrackerPrintThread(this));
- debug_output_thread_->Start();
-}
-
MemoryTrackerImpl::MemoryTrackerImpl()
: thread_filter_id_(kSbThreadInvalidId) {
total_bytes_allocated_.store(0);
@@ -393,16 +282,12 @@
MemoryTrackerImpl::~MemoryTrackerImpl() {
// If we are currently hooked into allocation tracking...
if (global_hooks_installed_) {
- SbMemorySetReporter(NULL);
+ RemoveGlobalTrackingHooks();
// For performance reasons no locking is used on the tracker.
// Therefore give enough time for other threads to exit this tracker
// before fully destroying this object.
SbThreadSleep(250 * kSbTimeMillisecond); // 250 millisecond wait.
}
- if (debug_output_thread_) {
- debug_output_thread_->Join();
- debug_output_thread_.reset();
- }
}
bool MemoryTrackerImpl::AddMemoryTracking(const void* memory, size_t size) {
@@ -447,6 +332,9 @@
AddAllocationBytes(size);
group->AddAllocation(size);
} else {
+ // Handles the case where the memory hasn't been properly been reported
+ // released. This is less serious than you would think because the memory
+ // allocator in the system will recycle the memory and it will come back.
AllocationRecord unexpected_alloc;
atomic_allocation_map_.Get(memory, &unexpected_alloc);
AllocationGroup* prev_group = unexpected_alloc.allocation_group;
@@ -458,12 +346,16 @@
prev_group_name = "none";
}
- SB_DCHECK(added)
- << "\nUnexpected condition, previous allocation was not removed:\n"
- << "\tprevious alloc group: " << prev_group_name << "\n"
- << "\tnew alloc group: " << group->name() << "\n"
- << "\tprevious size: " << unexpected_alloc.size << "\n"
- << "\tnew size: " << size << "\n";
+ if (!added) {
+ std::stringstream ss;
+ ss << "\nUnexpected condition, previous allocation was not removed:\n"
+ << "\tprevious alloc group: " << prev_group_name << "\n"
+ << "\tnew alloc group: " << group->name() << "\n"
+ << "\tprevious size: " << unexpected_alloc.size << "\n"
+ << "\tnew size: " << size << "\n";
+
+ SbLogRaw(ss.str().c_str());
+ }
}
return added;
}
@@ -537,5 +429,115 @@
owner_->SetMemoryTrackingEnabled(prev_value_);
}
+
+MemoryTrackerPrintThread::MemoryTrackerPrintThread(
+ MemoryTracker* memory_tracker)
+ : SimpleThread("MemoryTrackerPrintThread"),
+ finished_(false),
+ memory_tracker_(memory_tracker) {
+ Start();
+}
+
+MemoryTrackerPrintThread::~MemoryTrackerPrintThread() {
+ Cancel();
+ Join();
+}
+
+void MemoryTrackerPrintThread::Cancel() {
+ finished_.store(true);
+}
+
+void MemoryTrackerPrintThread::Run() {
+ struct NoMemTracking {
+ NoMemTracking(MemoryTracker* owner) : owner_(owner) {
+ prev_val_ = owner_->IsMemoryTrackingEnabled();
+ owner_->SetMemoryTrackingEnabled(false);
+ }
+ ~NoMemTracking() { owner_->SetMemoryTrackingEnabled(prev_val_); }
+
+ bool prev_val_;
+ MemoryTracker* owner_;
+ };
+
+ while (!finished_.load()) {
+ NoMemTracking no_mem_tracking_in_this_scope(memory_tracker_);
+
+ // std::map<std::string, const AllocationGroup*> output;
+ // typedef std::map<std::string, const AllocationGroup*>::const_iterator
+ // MapIt;
+ std::vector<const AllocationGroup*> vector_output;
+ memory_tracker_->GetAllocationGroups(&vector_output);
+
+ typedef std::map<std::string, const AllocationGroup*> Map;
+ typedef Map::const_iterator MapIt;
+
+ Map output;
+ for (int i = 0; i < vector_output.size(); ++i) {
+ const AllocationGroup* group = vector_output[i];
+ output[group->name()] = group;
+ }
+
+ int32_t num_allocs = 0;
+ int64_t total_bytes = 0;
+
+ struct F {
+ static void PrintRow(std::stringstream* ss,
+ const std::string& v1,
+ const std::string& v2,
+ const std::string& v3) {
+ ss->width(20);
+ *ss << std::left << v1;
+ ss->width(13);
+ *ss << std::right << v2 << " ";
+ ss->width(10);
+ *ss << std::right << v3 << "\n";
+ }
+ };
+
+ if (memory_tracker_->IsMemoryTrackingEnabled()) {
+ // If this isn't true then it would cause an infinite loop. The
+ // following will likely crash.
+ SB_DCHECK(false) << "Unexpected, memory tracking should be disabled.";
+ }
+
+ std::stringstream ss;
+
+ F::PrintRow(&ss, "NAME", "BYTES", "NUM ALLOCS");
+ ss << "---------------------------------------------\n";
+ for (MapIt it = output.begin(); it != output.end(); ++it) {
+ const AllocationGroup* group = it->second;
+ if (!group) {
+ continue;
+ }
+
+ int32_t num_group_allocs = -1;
+ int64_t total_group_bytes = -1;
+
+ group->GetAggregateStats(&num_group_allocs, &total_group_bytes);
+ SB_DCHECK(-1 != num_group_allocs);
+ SB_DCHECK(-1 != total_group_bytes);
+ num_allocs += num_group_allocs;
+ total_bytes += total_group_bytes;
+
+ F::PrintRow(&ss,
+ it->first,
+ NumberFormatWithCommas(total_group_bytes),
+ NumberFormatWithCommas(num_group_allocs));
+ }
+ ss << "---------------------------------------------\n";
+
+ std::stringstream final_ss;
+ final_ss << "\n"
+ << "Total Bytes Allocated: "
+ << NumberFormatWithCommas(total_bytes) << "\n"
+ << "Total allocations: "
+ << NumberFormatWithCommas(num_allocs) << "\n\n" << ss.str();
+
+ SbLogRaw(final_ss.str().c_str());
+
+ SbThreadSleep(1000 * 1000);
+ }
+}
+
} // namespace analytics
} // namespace nb
diff --git a/src/nb/analytics/memory_tracker_impl.h b/src/nb/analytics/memory_tracker_impl.h
index 7508a71..1575e7b 100644
--- a/src/nb/analytics/memory_tracker_impl.h
+++ b/src/nb/analytics/memory_tracker_impl.h
@@ -60,6 +60,8 @@
// generated for the supplied allocation which can be queried immediately
// with GetMemoryTracking(...).
bool InstallGlobalTrackingHooks() SB_OVERRIDE {
+ if (global_hooks_installed_)
+ return true;
global_hooks_installed_ = true;
bool ok = SbMemorySetReporter(GetMemoryReporter());
ok |= NbSetMemoryScopeReporter(GetMemoryScopeReporter());
@@ -123,10 +125,6 @@
// allocations.
void SetThreadFilter(SbThreadId tid);
bool IsCurrentThreadAllowedToReport() const;
- // Spawns a thread who's lifetime is coupled to that of this owning
- // MemoryTrackerImpl. This thread will output the state of the memory
- // periodically.
- void Debug_EnablePrintOutThread();
private:
struct DisableMemoryTrackingInScope {
@@ -169,7 +167,6 @@
AtomicStringAllocationGroupMap alloc_group_map_;
atomic_int64_t total_bytes_allocated_;
- scoped_ptr<SimpleThread> debug_output_thread_;
// THREAD LOCAL SECTION.
ThreadLocalBoolean memory_deletion_enabled_tls_;
@@ -178,6 +175,22 @@
bool global_hooks_installed_;
};
+// Start() is called when this object is created, and Cancel() & Join() are
+// called during destruction.
+class MemoryTrackerPrintThread : private SimpleThread {
+ public:
+ MemoryTrackerPrintThread(MemoryTracker* memory_tracker);
+ virtual ~MemoryTrackerPrintThread() SB_OVERRIDE;
+
+ // Overridden so that the thread can exit gracefully.
+ virtual void Cancel() SB_OVERRIDE;
+ virtual void Run() SB_OVERRIDE;
+
+ private:
+ atomic_bool finished_;
+ MemoryTracker* memory_tracker_;
+};
+
} // namespace analytics
} // namespace nb
diff --git a/src/nb/analytics/memory_tracker_impl_test.cc b/src/nb/analytics/memory_tracker_impl_test.cc
index 8236447..309d771 100644
--- a/src/nb/analytics/memory_tracker_impl_test.cc
+++ b/src/nb/analytics/memory_tracker_impl_test.cc
@@ -380,14 +380,14 @@
}
const bool kCaching = false;
NbMemoryScopeInfo memory_scope = {
- 0, "MyName", __FILE__,
+ NULL, "MyName", __FILE__,
__LINE__, __FUNCTION__, false}; // false to disallow caching.
// Pushing the memory scope should trigger the caching operation to be
// attempted. However, because caching was explicitly disabled this handle
// should retain the value of 0.
NbPushMemoryScope(&memory_scope);
- EXPECT_EQ_NO_TRACKING(memory_scope.cached_handle_, uintptr_t(0));
+ EXPECT_TRUE_NO_TRACKING(memory_scope.cached_handle_ == NULL);
// ... and still assert that the group was created with the expected name.
AllocationGroup* group = memory_tracker()->GetAllocationGroup("MyName");
@@ -405,18 +405,18 @@
return;
}
NbMemoryScopeInfo memory_scope = {
- 0, // Cached handle.
+ NULL, // Cached handle.
"MyName", // Memory scope name.
__FILE__, __LINE__, __FUNCTION__,
true // Allows caching.
};
NbPushMemoryScope(&memory_scope);
- EXPECT_TRUE_NO_TRACKING(memory_scope.cached_handle_ != uintptr_t(0));
+ EXPECT_TRUE_NO_TRACKING(memory_scope.cached_handle_ != NULL);
AllocationGroup* group = memory_tracker()->GetAllocationGroup("MyName");
- EXPECT_TRUE_NO_TRACKING(memory_scope.cached_handle_ ==
- reinterpret_cast<uintptr_t>(group));
+ EXPECT_EQ_NO_TRACKING(memory_scope.cached_handle_,
+ static_cast<void*>(group));
}
// Tests the expectation that the macro TRACK_MEMORY_SCOPE will capture the
@@ -793,7 +793,7 @@
<< "This pointer should not be in the map.";
}
- NoMemTracking no_tracking_in_scope; // DEBUG!!
+ NoMemTracking no_tracking_in_scope;
allocated_pts_[memory] = AllocationRecord(alloc_size, current_group);
}
diff --git a/src/nb/memory_scope.h b/src/nb/memory_scope.h
index 9c9446b..d7d37bf 100644
--- a/src/nb/memory_scope.h
+++ b/src/nb/memory_scope.h
@@ -17,8 +17,6 @@
#ifndef NB_MEMORY_SCOPE_H_
#define NB_MEMORY_SCOPE_H_
-#include "starboard/types.h"
-
///////////////////////////////////////////////////////////////////////////////
// Macros to define the memory scope objects. These are objects that are used
// to annotate sections of the code base as belonging to a particular memory
@@ -58,13 +56,13 @@
// tracking.
#define TRACK_MEMORY_STATIC_CACHED_IMPL_2(Str, FileStr, LineNum, FuncStr) \
static NbMemoryScopeInfo memory_scope_handle_##LineNum = \
- { 0, Str, FileStr, LineNum, FuncStr, true }; \
+ { NULL, Str, FileStr, LineNum, FuncStr, true }; \
NbPushMemoryScope(&memory_scope_handle_##LineNum); \
NbPopMemoryScopeOnScopeEnd pop_on_scope_end_##LineNum;
#define TRACK_MEMORY_STATIC_NOT_CACHED_IMPL_2(Str, FileStr, LineNum, FuncStr) \
NbMemoryScopeInfo memory_scope_handle_##LineNum = { \
- 0, Str, FileStr, LineNum, FuncStr, false}; \
+ NULL, Str, FileStr, LineNum, FuncStr, false}; \
NbPushMemoryScope(&memory_scope_handle_##LineNum); \
NbPopMemoryScopeOnScopeEnd pop_on_scope_end_##LineNum;
@@ -109,7 +107,7 @@
// cached_handle_ allows a cached result of the the fields represented in
// this struct to be generated and the handle be placed into this field.
// See also allows_caching_.
- uintptr_t cached_handle_;
+ void* cached_handle_;
// Represents the name of the memory scope. I.E. "Javascript" or "Gfx".
const char* memory_scope_name_;
diff --git a/src/nb/nb.gyp b/src/nb/nb.gyp
index 855c800..360ac03 100644
--- a/src/nb/nb.gyp
+++ b/src/nb/nb.gyp
@@ -20,43 +20,45 @@
'variables': {
'includes_starboard': 1,
},
- 'sources': [
- 'allocator.h',
- 'allocator_decorator.cc',
- 'allocator_decorator.h',
- 'analytics/memory_tracker.cc',
- 'analytics/memory_tracker.h',
- 'analytics/memory_tracker_impl.cc',
- 'analytics/memory_tracker_impl.h',
- 'analytics/memory_tracker_helpers.cc',
- 'analytics/memory_tracker_helpers.h',
- 'atomic.h',
- 'fixed_no_free_allocator.cc',
- 'fixed_no_free_allocator.h',
- 'hash.cc',
- 'hash.h',
- 'memory_pool.cc',
- 'memory_pool.h',
- 'memory_scope.cc',
- 'memory_scope.h',
- 'move.h',
- 'pointer_arithmetic.h',
- 'rect.h',
- 'ref_counted.cc',
- 'ref_counted.h',
- 'reuse_allocator.cc',
- 'reuse_allocator.h',
- 'scoped_ptr.h',
- 'thread_collision_warner.cc',
- 'thread_collision_warner.h',
- 'thread_local_object.h',
- ],
-
- 'dependencies': [
- '<(DEPTH)/starboard/starboard.gyp:starboard',
- ],
-
'conditions': [
+ ['OS=="starboard" or (OS=="lb_shell" and target_arch == "ps3")', {
+ 'sources': [
+ 'allocator.h',
+ 'allocator_decorator.cc',
+ 'allocator_decorator.h',
+ 'analytics/memory_tracker.cc',
+ 'analytics/memory_tracker.h',
+ 'analytics/memory_tracker_impl.cc',
+ 'analytics/memory_tracker_impl.h',
+ 'analytics/memory_tracker_helpers.cc',
+ 'analytics/memory_tracker_helpers.h',
+ 'atomic.h',
+ 'fixed_no_free_allocator.cc',
+ 'fixed_no_free_allocator.h',
+ 'hash.cc',
+ 'hash.h',
+ 'memory_pool.cc',
+ 'memory_pool.h',
+ 'memory_scope.cc',
+ 'memory_scope.h',
+ 'move.h',
+ 'pointer_arithmetic.h',
+ 'rect.h',
+ 'ref_counted.cc',
+ 'ref_counted.h',
+ 'reuse_allocator.cc',
+ 'reuse_allocator.h',
+ 'scoped_ptr.h',
+ 'simple_thread.cc',
+ 'simple_thread.h',
+ 'thread_collision_warner.cc',
+ 'thread_collision_warner.h',
+ 'thread_local_object.h',
+ ],
+ 'dependencies': [
+ '<(DEPTH)/starboard/starboard.gyp:starboard',
+ ],
+ }],
['target_arch == "ps4"', {
'sources': [
'kernel_contiguous_allocator_ps4.cc',
@@ -71,22 +73,26 @@
{
'target_name': 'nb_test',
'type': '<(gtest_target_type)',
- 'sources': [
- 'analytics/memory_tracker_helpers_test.cc',
- 'analytics/memory_tracker_impl_test.cc',
- 'analytics/memory_tracker_test.cc',
- 'atomic_test.cc',
- 'fixed_no_free_allocator_test.cc',
- 'memory_scope_test.cc',
- 'reuse_allocator_test.cc',
- 'run_all_unittests.cc',
- 'test_thread.h',
- 'thread_local_object_test.cc',
- ],
- 'dependencies': [
- 'nb',
- '<(DEPTH)/testing/gmock.gyp:gmock',
- '<(DEPTH)/testing/gtest.gyp:gtest',
+ 'conditions': [
+ ['OS=="starboard" or (OS=="lb_shell" and target_arch == "ps3")', {
+ 'sources': [
+ 'analytics/memory_tracker_helpers_test.cc',
+ 'analytics/memory_tracker_impl_test.cc',
+ 'analytics/memory_tracker_test.cc',
+ 'atomic_test.cc',
+ 'fixed_no_free_allocator_test.cc',
+ 'memory_scope_test.cc',
+ 'reuse_allocator_test.cc',
+ 'run_all_unittests.cc',
+ 'test_thread.h',
+ 'thread_local_object_test.cc',
+ ],
+ 'dependencies': [
+ 'nb',
+ '<(DEPTH)/testing/gmock.gyp:gmock',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ ],
+ }]
],
},
{
diff --git a/src/nb/simple_thread.cc b/src/nb/simple_thread.cc
new file mode 100644
index 0000000..7bfea39
--- /dev/null
+++ b/src/nb/simple_thread.cc
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "nb/simple_thread.h"
+
+#include "starboard/mutex.h"
+#include "starboard/thread.h"
+#include "starboard/types.h"
+#include "starboard/log.h"
+
+namespace nb {
+
+SimpleThread::SimpleThread(const std::string& name)
+ : thread_(kSbThreadInvalid), name_(name),
+ join_called_(false) {}
+
+SimpleThread::~SimpleThread() {
+ SB_DCHECK(join_called_.load()) << "Join not called on thread.";
+}
+
+void SimpleThread::Start() {
+ SbThreadEntryPoint entry_point = ThreadEntryPoint;
+
+ thread_ = SbThreadCreate(0, // default stack_size.
+ kSbThreadNoPriority, // default priority.
+ kSbThreadNoAffinity, // default affinity.
+ true, // joinable.
+ name_.c_str(), entry_point, this);
+
+ // SbThreadCreate() above produced an invalid thread handle.
+ SB_DCHECK(thread_ != kSbThreadInvalid);
+ return;
+}
+
+void* SimpleThread::ThreadEntryPoint(void* context) {
+ SimpleThread* this_ptr = static_cast<SimpleThread*>(context);
+ this_ptr->Run();
+ return NULL;
+}
+
+void SimpleThread::Join() {
+ SB_DCHECK(join_called_.load() == false);
+ if (!SbThreadJoin(thread_, NULL)) {
+ SB_DCHECK(false) << "Could not join thread.";
+ }
+ join_called_.store(true);
+}
+
+} // namespace nb
diff --git a/src/nb/simple_thread.h b/src/nb/simple_thread.h
new file mode 100644
index 0000000..ce3bbfe
--- /dev/null
+++ b/src/nb/simple_thread.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NB_THREAD_H_
+#define NB_THREAD_H_
+
+#include <string>
+
+#include "nb/atomic.h"
+#include "starboard/thread.h"
+#include "starboard/types.h"
+
+namespace nb {
+
+class SimpleThread {
+ public:
+ explicit SimpleThread(const std::string& name);
+ virtual ~SimpleThread();
+
+ // Subclasses should override the Run method.
+ virtual void Run() = 0;
+
+ // Signals to the thread to break out of it's loop.
+ virtual void Cancel() {}
+
+ // Called by the main thread, this will cause Run() to be invoked
+ // on another thread.
+ void Start();
+ // Destroys the threads resources.
+ void Join();
+
+ private:
+ static void* ThreadEntryPoint(void* context);
+
+ const std::string name_;
+ SbThread thread_;
+ atomic_bool join_called_;
+
+ SB_DISALLOW_COPY_AND_ASSIGN(SimpleThread);
+};
+
+} // namespace nb
+
+#endif // NB_THREAD_H_
diff --git a/src/net/socket/ssl_client_socket_openssl.cc b/src/net/socket/ssl_client_socket_openssl.cc
index 1adcfb5..1505cb0 100644
--- a/src/net/socket/ssl_client_socket_openssl.cc
+++ b/src/net/socket/ssl_client_socket_openssl.cc
@@ -1092,7 +1092,7 @@
DCHECK(recv_buffer_);
int ret = BIO_write(transport_bio_, recv_buffer_->data(), result);
// A write into a memory BIO should always succeed.
- CHECK_EQ(result, ret);
+ DCHECK_EQ(result, ret);
}
recv_buffer_ = NULL;
transport_recv_busy_ = false;
diff --git a/src/starboard/blitter.h b/src/starboard/blitter.h
index e807f63..3ed8258 100644
--- a/src/starboard/blitter.h
+++ b/src/starboard/blitter.h
@@ -513,8 +513,7 @@
// This function is not thread-safe.
//
// |out_pixel_data|: A pointer to a region of memory with a size of
-// (surface_height * |pitch_in_bytes| *
-// SbBlitterBytesPerPixelForFormat(pixel_format) bytes.
+// surface_height * |pitch_in_bytes| bytes.
SB_EXPORT bool SbBlitterDownloadSurfacePixels(
SbBlitterSurface surface,
SbBlitterPixelDataFormat pixel_format,
diff --git a/src/starboard/build/copy_data.py b/src/starboard/build/copy_data.py
index cd1d6a2..c19c020 100644
--- a/src/starboard/build/copy_data.py
+++ b/src/starboard/build/copy_data.py
@@ -14,7 +14,6 @@
# limitations under the License.
# This file is based on build/copy_test_data_ios.py
-
"""Copies data files or directories into a given output directory.
Since the output of this script is intended to be use by GYP, all resulting
@@ -29,6 +28,12 @@
if os.name == 'nt':
import win32api
+# The name of an environment variable that when set to |'1'|, signals to us
+# that we should log all output directories that we have staged a copy to to
+# our stderr output. This output could then, for example, be captured and
+# analyzed by an external tool that is interested in these directories.
+_ShouldLogEnvKey = 'STARBOARD_GYP_SHOULD_LOG_COPIES'
+
class WrongNumberOfArgumentsException(Exception):
pass
@@ -99,14 +104,27 @@
"""Called by GYP using pymod_do_main."""
parser = argparse.ArgumentParser()
parser.add_argument('-o', dest='output_dir', help='output directory')
- parser.add_argument('--inputs', action='store_true', dest='list_inputs',
- help='prints a list of all input files')
- parser.add_argument('--outputs', action='store_true', dest='list_outputs',
- help='prints a list of all output files')
- parser.add_argument('input_paths', metavar='path', nargs='+',
- help='path to an input file or directory')
+ parser.add_argument(
+ '--inputs',
+ action='store_true',
+ dest='list_inputs',
+ help='prints a list of all input files')
+ parser.add_argument(
+ '--outputs',
+ action='store_true',
+ dest='list_outputs',
+ help='prints a list of all output files')
+ parser.add_argument(
+ 'input_paths',
+ metavar='path',
+ nargs='+',
+ help='path to an input file or directory')
options = parser.parse_args(argv)
+ if os.environ.get(_ShouldLogEnvKey, None) == '1':
+ if options.output_dir is not None:
+ print >> sys.stderr, 'COPY_LOG:', options.output_dir
+
escaped_files = [EscapePath(x) for x in options.input_paths]
files_to_copy = CalcInputs(escaped_files)
if options.list_inputs:
@@ -133,5 +151,6 @@
print result
return 0
+
if __name__ == '__main__':
sys.exit(main(sys.argv))
diff --git a/src/starboard/configuration.h b/src/starboard/configuration.h
index 838b5e8..d21e420 100644
--- a/src/starboard/configuration.h
+++ b/src/starboard/configuration.h
@@ -405,20 +405,6 @@
#error "Your platform must define SB_HAS_THREAD_PRIORITY_SUPPORT."
#endif
-#if SB_HAS(THREAD_PRIORITY_SUPPORT)
-
-#if !defined(SB_HAS_REAL_TIME_PRIORITY_SUPPORT)
-#error "Your platform must define SB_HAS_REAL_TIME_PRIORITY_SUPPORT."
-#endif
-
-#else // SB_HAS(THREAD_PRIORITY_SUPPORT)
-
-#if defined(SB_HAS_REAL_TIME_PRIORITY_SUPPORT)
-#error "Don't define SB_HAS_REAL_TIME_PRIORITY_SUPPORT without priorities."
-#endif
-
-#endif // SB_HAS(THREAD_PRIORITY_SUPPORT)
-
#if !defined(SB_PREFERRED_RGBA_BYTE_ORDER)
// Legal values for SB_PREFERRED_RGBA_BYTE_ORDER are defined in this file above
// as SB_PREFERRED_RGBA_BYTE_ORDER_*.
diff --git a/src/starboard/creator/ci20x11/starboard_platform.gyp b/src/starboard/creator/ci20x11/starboard_platform.gyp
index 34be5d0..d061e46 100644
--- a/src/starboard/creator/ci20x11/starboard_platform.gyp
+++ b/src/starboard/creator/ci20x11/starboard_platform.gyp
@@ -192,6 +192,7 @@
'<(DEPTH)/starboard/shared/pthread/once.cc',
'<(DEPTH)/starboard/shared/pthread/thread_create.cc',
'<(DEPTH)/starboard/shared/pthread/thread_create_local_key.cc',
+ '<(DEPTH)/starboard/shared/pthread/thread_create_priority.h',
'<(DEPTH)/starboard/shared/pthread/thread_destroy_local_key.cc',
'<(DEPTH)/starboard/shared/pthread/thread_detach.cc',
'<(DEPTH)/starboard/shared/pthread/thread_get_current.cc',
diff --git a/src/starboard/creator/shared/configuration_public.h b/src/starboard/creator/shared/configuration_public.h
index bcf356a..4e572ea 100644
--- a/src/starboard/creator/shared/configuration_public.h
+++ b/src/starboard/creator/shared/configuration_public.h
@@ -148,7 +148,7 @@
// scheduling on threads.
#define SB_HAS_THREAD_PRIORITY_SUPPORT 0
-// --- Attribute Configuration -----------------------------------------------
+// --- Compiler Configuration ------------------------------------------------
// The platform's annotation for forcing a C function to be inlined.
#define SB_C_FORCE_INLINE __inline__ __attribute__((always_inline))
diff --git a/src/starboard/linux/shared/starboard_platform.gypi b/src/starboard/linux/shared/starboard_platform.gypi
index 1030459..ff6149d 100644
--- a/src/starboard/linux/shared/starboard_platform.gypi
+++ b/src/starboard/linux/shared/starboard_platform.gypi
@@ -178,6 +178,7 @@
'<(DEPTH)/starboard/shared/pthread/once.cc',
'<(DEPTH)/starboard/shared/pthread/thread_create.cc',
'<(DEPTH)/starboard/shared/pthread/thread_create_local_key.cc',
+ '<(DEPTH)/starboard/shared/pthread/thread_create_priority.h',
'<(DEPTH)/starboard/shared/pthread/thread_destroy_local_key.cc',
'<(DEPTH)/starboard/shared/pthread/thread_detach.cc',
'<(DEPTH)/starboard/shared/pthread/thread_get_current.cc',
diff --git a/src/starboard/linux/x64directfb/future/starboard_platform.gyp b/src/starboard/linux/x64directfb/future/starboard_platform.gyp
index 0f82c74..94f15db 100644
--- a/src/starboard/linux/x64directfb/future/starboard_platform.gyp
+++ b/src/starboard/linux/x64directfb/future/starboard_platform.gyp
@@ -22,6 +22,9 @@
'<(DEPTH)/starboard/shared/stub/decode_target_destroy.cc',
'<(DEPTH)/starboard/shared/stub/decode_target_get_format.cc',
'<(DEPTH)/starboard/shared/stub/decode_target_get_plane_blitter.cc',
+ '<(DEPTH)/starboard/shared/stub/decode_target_is_opaque.cc',
+ '<(DEPTH)/starboard/shared/stub/image_decode.cc',
+ '<(DEPTH)/starboard/shared/stub/image_is_decode_supported.cc',
],
'defines': [
# This must be defined when building Starboard, and must not when
@@ -31,7 +34,6 @@
'dependencies': [
'<(DEPTH)/starboard/common/common.gyp:common',
'<(DEPTH)/starboard/linux/x64directfb/starboard_platform.gyp:starboard_platform',
- '<(DEPTH)/starboard/linux/x64directfb/starboard_platform.gyp:starboard_base_symbolize',
'<(DEPTH)/third_party/dlmalloc/dlmalloc.gyp:dlmalloc',
'<(DEPTH)/third_party/libevent/libevent.gyp:libevent',
],
diff --git a/src/starboard/linux/x64x11/sanitizer_options.cc b/src/starboard/linux/x64x11/sanitizer_options.cc
index 91d1d26..2ac2410 100644
--- a/src/starboard/linux/x64x11/sanitizer_options.cc
+++ b/src/starboard/linux/x64x11/sanitizer_options.cc
@@ -32,6 +32,9 @@
__attribute__((weak)) \
__attribute__((used))
+// Newline separated list of issues to suppress, see
+// http://clang.llvm.org/docs/AddressSanitizer.html#issue-suppression
+// http://llvm.org/svn/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_suppressions.cc
SANITIZER_HOOK_ATTRIBUTE const char* __lsan_default_suppressions() {
return "leak:egl_gallium.so\n";
}
diff --git a/src/starboard/nplb/microphone_get_speech_api_key_test.cc b/src/starboard/nplb/microphone_get_speech_api_key_test.cc
deleted file mode 100644
index 67b5347..0000000
--- a/src/starboard/nplb/microphone_get_speech_api_key_test.cc
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "starboard/microphone.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace starboard {
-namespace nplb {
-
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
-
-TEST(SbMicrophoneGetSpeechApiKeyTest, SunnyDay) {
- const char* speech_api_key = SbMicrophoneGetSpeechApiKey();
-
- ASSERT_NE(speech_api_key, static_cast<const char*>(NULL));
- EXPECT_NE(speech_api_key[0], '\0');
-}
-
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
-
-} // namespace nplb
-} // namespace starboard
diff --git a/src/starboard/raspi/1/starboard_platform.gyp b/src/starboard/raspi/1/starboard_platform.gyp
index 4f3603f..fb203a6 100644
--- a/src/starboard/raspi/1/starboard_platform.gyp
+++ b/src/starboard/raspi/1/starboard_platform.gyp
@@ -41,6 +41,7 @@
'<(DEPTH)/starboard/raspi/shared/open_max/open_max_component.h',
'<(DEPTH)/starboard/raspi/shared/open_max/video_decoder.cc',
'<(DEPTH)/starboard/raspi/shared/open_max/video_decoder.h',
+ '<(DEPTH)/starboard/raspi/shared/thread_create_priority.cc',
'<(DEPTH)/starboard/raspi/shared/window_create.cc',
'<(DEPTH)/starboard/raspi/shared/window_destroy.cc',
'<(DEPTH)/starboard/raspi/shared/window_get_platform_handle.cc',
@@ -202,6 +203,7 @@
'<(DEPTH)/starboard/shared/pthread/once.cc',
'<(DEPTH)/starboard/shared/pthread/thread_create.cc',
'<(DEPTH)/starboard/shared/pthread/thread_create_local_key.cc',
+ '<(DEPTH)/starboard/shared/pthread/thread_create_priority.h',
'<(DEPTH)/starboard/shared/pthread/thread_destroy_local_key.cc',
'<(DEPTH)/starboard/shared/pthread/thread_detach.cc',
'<(DEPTH)/starboard/shared/pthread/thread_get_current.cc',
diff --git a/src/starboard/raspi/shared/configuration_public.h b/src/starboard/raspi/shared/configuration_public.h
index 0d4b088..ba25c7f 100644
--- a/src/starboard/raspi/shared/configuration_public.h
+++ b/src/starboard/raspi/shared/configuration_public.h
@@ -78,7 +78,7 @@
// broken in that higher priority threads do not always have priority over lower
// priority threads. It looks like the thread created last will always have the
// highest priority.
-#define SB_HAS_THREAD_PRIORITY_SUPPORT 0
+#define SB_HAS_THREAD_PRIORITY_SUPPORT 1
// --- Attribute Configuration -----------------------------------------------
diff --git a/src/starboard/raspi/shared/thread_create_priority.cc b/src/starboard/raspi/shared/thread_create_priority.cc
new file mode 100644
index 0000000..b36cd1f
--- /dev/null
+++ b/src/starboard/raspi/shared/thread_create_priority.cc
@@ -0,0 +1,71 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/shared/pthread/thread_create_priority.h"
+
+#include <sched.h>
+
+#include "starboard/log.h"
+
+namespace starboard {
+namespace shared {
+namespace pthread {
+
+#if SB_HAS(THREAD_PRIORITY_SUPPORT)
+void ThreadSetPriority(SbThreadPriority priority) {
+ // Note that use of sched_setscheduler() has been found to be more reliably
+ // supported than pthread_setschedparam(), so we are using that.
+
+ // Use different schedulers according to priority. This is preferred over
+ // using SCHED_RR for all threads because the scheduler time slice is too
+ // high (defaults to 100ms) for the desired threading behavior.
+ struct sched_param thread_sched_param;
+ int result = 0;
+
+ thread_sched_param.sched_priority = 0;
+ switch (priority) {
+ case kSbThreadPriorityLowest:
+ case kSbThreadPriorityLow:
+ result = sched_setscheduler(0, SCHED_IDLE, &thread_sched_param);
+ break;
+ case kSbThreadNoPriority:
+ case kSbThreadPriorityNormal:
+ result = sched_setscheduler(0, SCHED_OTHER, &thread_sched_param);
+ break;
+ case kSbThreadPriorityHigh:
+ thread_sched_param.sched_priority = 1;
+ result = sched_setscheduler(0, SCHED_RR, &thread_sched_param);
+ break;
+ case kSbThreadPriorityHighest:
+ thread_sched_param.sched_priority = 2;
+ result = sched_setscheduler(0, SCHED_RR, &thread_sched_param);
+ break;
+ case kSbThreadPriorityRealTime:
+ thread_sched_param.sched_priority = 3;
+ result = sched_setscheduler(0, SCHED_RR, &thread_sched_param);
+ break;
+ default:
+ SB_NOTREACHED();
+ break;
+ }
+
+ if (result != 0) {
+ SB_NOTREACHED();
+ }
+}
+#endif // SB_HAS(THREAD_PRIORITY_SUPPORT)
+
+} // namespace pthread
+} // namespace shared
+} // namespace starboard
diff --git a/src/starboard/shared/pthread/thread_create.cc b/src/starboard/shared/pthread/thread_create.cc
index f2ab13d..6cc9028 100644
--- a/src/starboard/shared/pthread/thread_create.cc
+++ b/src/starboard/shared/pthread/thread_create.cc
@@ -21,68 +21,25 @@
#include "starboard/log.h"
#include "starboard/shared/pthread/is_success.h"
+#include "starboard/shared/pthread/thread_create_priority.h"
#include "starboard/string.h"
-#if SB_HAS(THREAD_PRIORITY_SUPPORT) && SB_HAS(REAL_TIME_PRIORITY_SUPPORT)
-#if !defined(_POSIX_PRIORITY_SCHEDULING)
-#error "The _POSIX_PRIORITY_SCHEDULING define indicates that a pthreads \
-system supports thread priorities, however this define is not \
-defined on this system, contradicting the Starboard configuration \
-indicating that priority scheduling is supported."
-#endif // !defined(_POSIX_PRIORITY_SCHEDULING)
-#endif // SB_HAS(THREAD_PRIORITY_SUPPORT) && SB_HAS(REAL_TIME_PRIORITY_SUPPORT)
+namespace starboard {
+namespace shared {
+namespace pthread {
+
+#if !SB_HAS(THREAD_PRIORITY_SUPPORT)
+// Default implementation without thread priority support
+void ThreadSetPriority(SbThreadPriority /* priority */) {
+}
+#endif
+
+} // namespace pthread
+} // namespace shared
+} // namespace starboard
namespace {
-#if SB_HAS(THREAD_PRIORITY_SUPPORT)
-#if SB_HAS(REAL_TIME_PRIORITY_SUPPORT)
-
-int SbThreadPriorityToPthread(SbThreadPriority priority) {
- switch (priority) {
- case kSbThreadPriorityLowest:
- SB_NOTREACHED() << "Lowest priority threads should use SCHED_OTHER.";
- return 0;
- break;
- case kSbThreadPriorityLow: return 2;
- case kSbThreadNoPriority:
- // Fall through on purpose to default to kThreadPriority_Normal.
- case kSbThreadPriorityNormal: return 3;
- case kSbThreadPriorityHigh: return 4;
- case kSbThreadPriorityHighest: return 5;
- case kSbThreadPriorityRealTime: return 6;
- default:
- SB_NOTREACHED();
- return 0;
- }
-}
-
-#else // SB_HAS(REAL_TIME_PRIORITY_SUPPORT)
-
-int SbThreadPriorityToNice(SbThreadPriority priority) {
- switch (priority) {
- case kSbThreadPriorityLowest:
- return 19;
- case kSbThreadPriorityLow:
- return 18;
- case kSbThreadNoPriority:
- // Fall through on purpose to default to kThreadPriority_Normal.
- case kSbThreadPriorityNormal:
- return 10;
- case kSbThreadPriorityHigh:
- return 2;
- case kSbThreadPriorityHighest:
- return 1;
- case kSbThreadPriorityRealTime:
- return 0;
- default:
- SB_NOTREACHED();
- return 0;
- }
-}
-
-#endif // #if SB_HAS(REAL_TIME_PRIORITY_SUPPORT)
-#endif // #if SB_HAS(THREAD_PRIORITY_SUPPORT)
-
struct ThreadParams {
SbThreadAffinity affinity;
SbThreadEntryPoint entry_point;
@@ -100,26 +57,7 @@
SbThreadSetName(thread_params->name);
}
-#if SB_HAS(THREAD_PRIORITY_SUPPORT)
-#if SB_HAS(REAL_TIME_PRIORITY_SUPPORT)
- // Use Linux' regular scheduler for lowest priority threads. Real-time
- // priority threads (of any priority) will always have priority over
- // non-real-time threads (e.g. threads whose scheduler is setup to be
- // SCHED_OTHER, the default scheduler).
- if (thread_params->priority != kSbThreadPriorityLowest) {
- // Note that use of sched_setscheduler() has been found to be more reliably
- // supported than pthread_setschedparam(), so we are using that.
- struct sched_param thread_sched_param;
- thread_sched_param.sched_priority =
- SbThreadPriorityToPthread(thread_params->priority);
- sched_setscheduler(0, SCHED_FIFO, &thread_sched_param);
- }
-#else // #if SB_HAS(REAL_TIME_PRIORITY_SUPPORT)
- // If we don't have real time thread priority support, then set the nice
- // value instead for soft priority support.
- setpriority(PRIO_PROCESS, 0, SbThreadPriorityToNice(thread_params->priority));
-#endif // SB_HAS(REAL_TIME_PRIORITY_SUPPORT)
-#endif // SB_HAS(THREAD_PRIORITY_SUPPORT)
+ starboard::shared::pthread::ThreadSetPriority(thread_params->priority);
delete thread_params;
diff --git a/src/starboard/shared/pthread/thread_create_priority.h b/src/starboard/shared/pthread/thread_create_priority.h
new file mode 100644
index 0000000..1c67ef5
--- /dev/null
+++ b/src/starboard/shared/pthread/thread_create_priority.h
@@ -0,0 +1,34 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef STARBOARD_SHARED_PTHREAD_THREAD_CREATE_PRIORITY_H_
+#define STARBOARD_SHARED_PTHREAD_THREAD_CREATE_PRIORITY_H_
+
+#include "starboard/thread.h"
+
+namespace starboard {
+namespace shared {
+namespace pthread {
+
+// Set priority of the current thread.
+//
+// Implement this in a platform-specific thread_create_priority.cc if the
+// platform SB_HAS(THREAD_PRIORITY_SUPPORT)
+void ThreadSetPriority(SbThreadPriority priority);
+
+} // namespace pthread
+} // namespace shared
+} // namespace starboard
+
+#endif // STARBOARD_SHARED_PTHREAD_THREAD_CREATE_PRIORITY_H_
diff --git a/src/starboard/shared/stub/microphone_get_speech_api_key.cc b/src/starboard/shared/stub/microphone_get_speech_api_key.cc
deleted file mode 100644
index 98038b4..0000000
--- a/src/starboard/shared/stub/microphone_get_speech_api_key.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2016 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "starboard/microphone.h"
-
-#if SB_HAS(MICROPHONE) && SB_VERSION(2)
-
-const char* SbMicrophoneGetSpeechApiKey() {
- return "";
-}
-
-#endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
diff --git a/src/starboard/stub/configuration_public.h b/src/starboard/stub/configuration_public.h
index 036175e..c342ff9 100644
--- a/src/starboard/stub/configuration_public.h
+++ b/src/starboard/stub/configuration_public.h
@@ -148,7 +148,7 @@
#define SB_IS_WCHAR_T_SIGNED 1
#endif
-// --- Attribute Configuration -----------------------------------------------
+// --- Compiler Configuration ------------------------------------------------
// The platform's annotation for forcing a C function to be inlined.
#define SB_C_FORCE_INLINE __inline__ __attribute__((always_inline))
@@ -169,6 +169,11 @@
// the current linking unit.
#define SB_IMPORT_PLATFORM
+// On some platforms the __GNUC__ is defined even though parts of the
+// functionality are missing. Setting this to non-zero allows disabling missing
+// functionality encountered.
+#undef SB_HAS_QUIRK_COMPILER_SAYS_GNUC_BUT_ISNT
+
// --- Extensions Configuration ----------------------------------------------
// GCC/Clang doesn't define a long long hash function, except for Android and
diff --git a/src/third_party/icu/source/common/cmemory.c b/src/third_party/icu/source/common/cmemory.c
index bef32cb..231a79c 100644
--- a/src/third_party/icu/source/common/cmemory.c
+++ b/src/third_party/icu/source/common/cmemory.c
@@ -67,7 +67,7 @@
(*pFree)(pContext, buffer);
} else {
#if defined(STARBOARD)
- SbMemoryFree(buffer);
+ SbMemoryDeallocate(buffer);
#else
free(buffer);
#endif
@@ -94,7 +94,7 @@
(*pFree)(pContext, buffer);
} else {
#if defined(STARBOARD)
- SbMemoryFree(buffer);
+ SbMemoryDeallocate(buffer);
#else
free(buffer);
#endif
diff --git a/src/third_party/mozjs/cobalt_config/include/js-confdefs.h b/src/third_party/mozjs/cobalt_config/include/js-confdefs.h
index 28f22d6..6a5d596 100644
--- a/src/third_party/mozjs/cobalt_config/include/js-confdefs.h
+++ b/src/third_party/mozjs/cobalt_config/include/js-confdefs.h
@@ -8,7 +8,7 @@
#include "starboard/configuration.h"
-#if SB_IS(64_BIT)
+#if SB_HAS(64_BIT_POINTERS)
#define JS_BYTES_PER_WORD 8
#define JS_BITS_PER_WORD_LOG2 6
#else
diff --git a/src/third_party/mozjs/js/public/Anchor.h b/src/third_party/mozjs/js/public/Anchor.h
index 0d458e6..623fbf0 100644
--- a/src/third_party/mozjs/js/public/Anchor.h
+++ b/src/third_party/mozjs/js/public/Anchor.h
@@ -11,6 +11,10 @@
#include "mozilla/Attributes.h"
+#if defined(STARBOARD)
+#include "starboard/configuration.h"
+#endif
+
class JSFunction;
class JSObject;
class JSScript;
@@ -110,10 +114,16 @@
void operator=(const Anchor &other) MOZ_DELETE;
};
+#if defined(STARBOARD)
+#if SB_HAS_QUIRK(COMPILER_SAYS_GNUC_BUT_ISNT)
+#define ENABLE_COMPILER_SAYS_GNUC_BUT_ISNT_WORKAROUND
+#endif
+#endif
+
template<typename T>
inline Anchor<T>::~Anchor()
{
-#ifdef __GNUC__
+#if defined(__GNUC__) && !defined(ENABLE_COMPILER_SAYS_GNUC_BUT_ISNT_WORKAROUND)
/*
* No code is generated for this. But because this is marked 'volatile', G++ will
* assume it has important side-effects, and won't delete it. (G++ never looks at
@@ -154,7 +164,7 @@
*/
volatile T sink;
sink = hold;
-#endif /* defined(__GNUC__) */
+#endif /* defined(__GNUC__) && !defined(ENABLE_COMPILER_SAYS_GNUC_BUT_ISNT_WORKAROUND) */
}
} // namespace JS
diff --git a/src/third_party/mozjs/js/public/CharacterEncoding.h b/src/third_party/mozjs/js/public/CharacterEncoding.h
index 7065a4b..29f55af 100644
--- a/src/third_party/mozjs/js/public/CharacterEncoding.h
+++ b/src/third_party/mozjs/js/public/CharacterEncoding.h
@@ -127,7 +127,7 @@
TwoByteCharsZ(jschar *chars, size_t length)
: Base(chars, length)
{
- JS_ASSERT(chars[length] = '\0');
+ JS_ASSERT(chars[length] == '\0');
}
};
diff --git a/src/third_party/mozjs/js/src/frontend/Parser.h b/src/third_party/mozjs/js/src/frontend/Parser.h
index f0a7fb8..1c6e0e9 100644
--- a/src/third_party/mozjs/js/src/frontend/Parser.h
+++ b/src/third_party/mozjs/js/src/frontend/Parser.h
@@ -326,7 +326,11 @@
LazyScript *lazyOuterFunction);
~Parser();
+#if defined(STARBOARD)
+ friend void MarkParser(JSTracer *trc, AutoGCRooter *parser);
+#else
friend void js::frontend::MarkParser(JSTracer *trc, AutoGCRooter *parser);
+#endif
const char *getFilename() const { return tokenStream.getFilename(); }
JSVersion versionNumber() const { return tokenStream.versionNumber(); }
diff --git a/src/third_party/mozjs/js/src/frontend/TokenStream.cpp b/src/third_party/mozjs/js/src/frontend/TokenStream.cpp
index 02da46f..21866fc 100644
--- a/src/third_party/mozjs/js/src/frontend/TokenStream.cpp
+++ b/src/third_party/mozjs/js/src/frontend/TokenStream.cpp
@@ -675,7 +675,7 @@
err.argumentsType = (flags & JSREPORT_UC) ? ArgumentsAreUnicode : ArgumentsAreASCII;
- if (!js_ExpandErrorArguments(cx, js_GetErrorMessage, NULL, errorNumber, &err.message,
+ if (!js_ExpandErrorArgumentsVA(cx, js_GetErrorMessage, NULL, errorNumber, &err.message,
&err.report, err.argumentsType, args))
{
return false;
diff --git a/src/third_party/mozjs/js/src/jsapi.cpp b/src/third_party/mozjs/js/src/jsapi.cpp
index 85b9856..4aa997b 100644
--- a/src/third_party/mozjs/js/src/jsapi.cpp
+++ b/src/third_party/mozjs/js/src/jsapi.cpp
@@ -5001,6 +5001,63 @@
typedef Vector<char, 8, TempAllocPolicy> FileContents;
+#if defined(STARBOARD)
+static bool
+ReadCompleteFile(JSContext *cx, SbFile file, FileContents &buffer)
+{
+ SbFileInfo info;
+ bool success = SbFileGetInfo(file, &info);
+ if (!success) {
+ return false;
+ }
+ const int64_t kFileSize = info.size;
+ buffer.resize(kFileSize);
+ if (SbFileReadAll(file, buffer.begin(), kFileSize) < 0) {
+ return false;
+ }
+
+ return true;
+}
+
+class AutoFile
+{
+ SbFile sb_file_;
+ public:
+ AutoFile() {}
+ ~AutoFile()
+ {
+ SbFileClose(sb_file_);
+ }
+ SbFile sb_file() const { return sb_file_; }
+ bool open(JSContext *cx, const char *filename);
+ bool readAll(JSContext *cx, FileContents &buffer)
+ {
+ return ReadCompleteFile(cx, sb_file_, buffer);
+ }
+};
+
+/*
+ * Open a source file for reading. Supports "-" and NULL to mean stdin. The
+ * return value must be fclosed unless it is stdin.
+ */
+bool
+AutoFile::open(JSContext *cx, const char *filename)
+{
+ // Starboard does not support stdin.
+ if (!filename || strcmp(filename, "-") == 0) {
+ return false;
+ } else {
+ sb_file_ = SbFileOpen(filename, kSbFileOpenOnly | kSbFileRead, NULL,
+ NULL);
+ if (!SbFileIsValid(sb_file_)) {
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_OPEN,
+ filename, "No such file or directory");
+ return false;
+ }
+ }
+ return true;
+}
+#else // #if defined(STARBOARD)
static bool
ReadCompleteFile(JSContext *cx, FILE *fp, FileContents &buffer)
{
@@ -5070,6 +5127,7 @@
return true;
}
+#endif // #if defined(STARBOARD)
JS::CompileOptions::CompileOptions(JSContext *cx, JSVersion version)
: principals(NULL),
diff --git a/src/third_party/mozjs/js/src/jsclone.cpp b/src/third_party/mozjs/js/src/jsclone.cpp
index 8df21bf..6869515 100644
--- a/src/third_party/mozjs/js/src/jsclone.cpp
+++ b/src/third_party/mozjs/js/src/jsclone.cpp
@@ -135,7 +135,8 @@
uint32_t tag = uint32_t(u >> 32);
if (tag == SCTAG_TRANSFER_MAP) {
u = LittleEndian::readUint64(point++);
- js_free(reinterpret_cast<void*>(u));
+ js_free(reinterpret_cast<void*>(
+ static_cast<uintptr_t>(u)));
} else {
// The only things in the transfer map should be
// SCTAG_TRANSFER_MAP tags paired with pointers. If we find
@@ -312,7 +313,11 @@
// 32 bits, so we create a 64 temporary and discard the unused bits.
uint64_t tmp;
bool ret = read(&tmp);
+#if defined(STARBOARD)
+ *p = reinterpret_cast<void*>(static_cast<uintptr_t>(tmp));
+#else
*p = reinterpret_cast<void*>(tmp);
+#endif
return ret;
}
diff --git a/src/third_party/mozjs/js/src/jscntxt.cpp b/src/third_party/mozjs/js/src/jscntxt.cpp
index d145786..24b00bd 100644
--- a/src/third_party/mozjs/js/src/jscntxt.cpp
+++ b/src/third_party/mozjs/js/src/jscntxt.cpp
@@ -743,7 +743,7 @@
* Returns true if the expansion succeeds (can fail if out of memory).
*/
JSBool
-js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
+js_ExpandErrorArgumentsVA(JSContext *cx, JSErrorCallback callback,
void *userRef, const unsigned errorNumber,
char **messagep, JSErrorReport *reportp,
ErrorArgumentsType argumentsType, va_list ap)
@@ -922,7 +922,7 @@
report.errorNumber = errorNumber;
PopulateReportBlame(cx, &report);
- if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
+ if (!js_ExpandErrorArgumentsVA(cx, callback, userRef, errorNumber,
&message, &report, argumentsType, ap)) {
return JS_FALSE;
}
@@ -933,7 +933,7 @@
js_free(message);
if (report.messageArgs) {
/*
- * js_ExpandErrorArguments owns its messageArgs only if it had to
+ * js_ExpandErrorArgumentsVA owns its messageArgs only if it had to
* inflate the arguments (from regular |char *|s).
*/
if (argumentsType == ArgumentsAreASCII) {
@@ -949,6 +949,21 @@
return warning;
}
+static bool
+js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
+ void *userRef, const unsigned errorNumber,
+ char **messagep, JSErrorReport *reportp,
+ ErrorArgumentsType argumentsType, ...)
+{
+ va_list ap;
+ va_start(ap, argumentsType);
+ bool expanded = js_ExpandErrorArgumentsVA(cx, callback, userRef, errorNumber,
+ messagep, reportp, argumentsType, ap);
+ va_end(ap);
+ return expanded;
+}
+
+
bool
js_ReportErrorNumberUCArray(JSContext *cx, unsigned flags, JSErrorCallback callback,
void *userRef, const unsigned errorNumber,
@@ -966,9 +981,8 @@
report.messageArgs = args;
char *message;
- va_list dummy;
if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
- &message, &report, ArgumentsAreUnicode, dummy)) {
+ &message, &report, ArgumentsAreUnicode)) {
return false;
}
diff --git a/src/third_party/mozjs/js/src/jscntxt.h b/src/third_party/mozjs/js/src/jscntxt.h
index d918d49..9339c07 100644
--- a/src/third_party/mozjs/js/src/jscntxt.h
+++ b/src/third_party/mozjs/js/src/jscntxt.h
@@ -2165,10 +2165,10 @@
const jschar **args);
extern JSBool
-js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
- void *userRef, const unsigned errorNumber,
- char **message, JSErrorReport *reportp,
- js::ErrorArgumentsType argumentsType, va_list ap);
+js_ExpandErrorArgumentsVA(JSContext *cx, JSErrorCallback callback,
+ void *userRef, const unsigned errorNumber,
+ char **message, JSErrorReport *reportp,
+ js::ErrorArgumentsType argumentsType, va_list ap);
#endif
namespace js {
diff --git a/src/third_party/mozjs/js/src/jslibmath.h b/src/third_party/mozjs/js/src/jslibmath.h
index 20aa19a..d077cbf 100644
--- a/src/third_party/mozjs/js/src/jslibmath.h
+++ b/src/third_party/mozjs/js/src/jslibmath.h
@@ -12,12 +12,23 @@
#include <math.h>
#include "jsnum.h"
+#if defined(STARBOARD)
+#include "starboard/configuration.h"
+#endif
+
/*
* Use system provided math routines.
*/
+#if defined(STARBOARD)
+#if SB_HAS_QUIRK(COMPILER_SAYS_GNUC_BUT_ISNT)
+#define ENABLE_COMPILER_SAYS_GNUC_BUT_ISNT_WORKAROUND
+#endif
+#endif
+
/* The right copysign function is not always named the same thing. */
-#ifdef __GNUC__
+// Check for quirk COMPILER_SAYS_GNUC_BUT_ISNT when picking copysign function.
+#if defined(__GNUC__) && !defined(ENABLE_COMPILER_SAYS_GNUC_BUT_ISNT_WORKAROUND)
#define js_copysign __builtin_copysign
#elif defined _WIN32
#define js_copysign _copysign
@@ -65,7 +76,7 @@
inline double
NumberMod(double a, double b) {
- if (b == 0)
+ if (b == 0)
return js_NaN;
return js_fmod(a, b);
}
diff --git a/src/third_party/mozjs/js/src/jstypedarray.cpp b/src/third_party/mozjs/js/src/jstypedarray.cpp
index 8019563..58f3ac5 100644
--- a/src/third_party/mozjs/js/src/jstypedarray.cpp
+++ b/src/third_party/mozjs/js/src/jstypedarray.cpp
@@ -34,7 +34,9 @@
#include "vm/GlobalObject-inl.h"
-# ifdef XP_WIN
+#if defined(STARBOARD)
+// empty
+# elif defined(XP_WIN)
# include "jswin.h"
# else
# include <sys/mman.h>
@@ -336,7 +338,7 @@
return true;
}
-#if defined(JS_ION) && defined(JS_CPU_X64)
+#if defined(JS_ION) && defined(JS_CPU_X64) && !defined(STARBOARD)
// To avoid dynamically checking bounds on each load/store, asm.js code relies
// on the SIGSEGV handler in AsmJSSignalHandlers.cpp. However, this only works
// if we can guarantee that *any* out-of-bounds access generates a fault. This
@@ -440,7 +442,7 @@
MOZ_CRASH();
#endif
}
-#else /* defined(JS_ION) && defined(JS_CPU_X64) */
+#else /* defined(JS_ION) && defined(JS_CPU_X64) && !defined(STARBOARD)*/
bool
ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer)
{
diff --git a/src/third_party/mozjs/js/src/jstypedarray.h b/src/third_party/mozjs/js/src/jstypedarray.h
index 4751d53..b8e1f99 100644
--- a/src/third_party/mozjs/js/src/jstypedarray.h
+++ b/src/third_party/mozjs/js/src/jstypedarray.h
@@ -371,14 +371,22 @@
public:
static Class class_;
+#if defined(STARBOARD)
+ template<Value ValueGetter(DataViewObject &view)>
+ static bool
+ getterImpl(JSContext *cx, CallArgs args);
+#endif
+
private:
static Class protoClass;
static inline bool is(const Value &v);
+#if !defined(STARBOARD)
template<Value ValueGetter(DataViewObject &view)>
static bool
getterImpl(JSContext *cx, CallArgs args);
+#endif
template<Value ValueGetter(DataViewObject &view)>
static JSBool
diff --git a/src/third_party/mozjs/js/src/memory_allocator_reporter.cpp b/src/third_party/mozjs/js/src/memory_allocator_reporter.cpp
index 9b879bb..a7d8efa 100644
--- a/src/third_party/mozjs/js/src/memory_allocator_reporter.cpp
+++ b/src/third_party/mozjs/js/src/memory_allocator_reporter.cpp
@@ -27,7 +27,11 @@
void* OffsetPointer(void* base, int64_t offset) {
uintptr_t base_as_int = reinterpret_cast<uintptr_t>(base);
- return reinterpret_cast<void*>(base_as_int + offset);
+#if defined(STARBOARD)
+ return reinterpret_cast<void*>(base_as_int + static_cast<uintptr_t>(offset));
+#else
+ return reinterpret_cast<void*>(base_as_int + offset);
+#endif
}
} // namespace
diff --git a/src/third_party/mozjs/js/src/shell/js.cpp b/src/third_party/mozjs/js/src/shell/js.cpp
index 03d0010..5987c08 100644
--- a/src/third_party/mozjs/js/src/shell/js.cpp
+++ b/src/third_party/mozjs/js/src/shell/js.cpp
@@ -69,12 +69,15 @@
#include <io.h> /* for isatty() */
#endif
-#ifdef XP_WIN
+#if defined(STARBOARD)
+#include "starboard/configuration.h"
+#define PATH_MAX (SB_FILE_MAX_PATH + 1)
+#elif defined(XP_WIN)
# include <io.h>
# include <direct.h>
# include "jswin.h"
# define PATH_MAX (MAX_PATH > _MAX_DIR ? MAX_PATH : _MAX_DIR)
-#elif !defined(STARBOARD)
+#else
# include <libgen.h>
#endif
diff --git a/src/third_party/mozjs/mfbt/Endian.h b/src/third_party/mozjs/mfbt/Endian.h
index 5d2f905..43d2c17 100644
--- a/src/third_party/mozjs/mfbt/Endian.h
+++ b/src/third_party/mozjs/mfbt/Endian.h
@@ -71,6 +71,10 @@
#include <string.h>
+#if defined(STARBOARD)
+#include "starboard/byte_swap.h"
+#endif
+
#if defined(_MSC_VER) && _MSC_VER >= 1300
# include <stdlib.h>
# pragma intrinsic(_byteswap_ushort)
@@ -184,7 +188,9 @@
{
static T swap(T value)
{
-#if defined(__clang__) || defined(__GNUC__)
+#if defined(STARBOARD)
+ return static_cast<T>(SbByteSwapU32(value));
+#elif defined(__clang__) || defined(__GNUC__)
return T(__builtin_bswap32(value));
#elif defined(_MSC_VER)
return T(_byteswap_ulong(value));
@@ -202,7 +208,9 @@
{
static inline T swap(T value)
{
-#if defined(__clang__) || defined(__GNUC__)
+#if defined(STARBOARD)
+ return static_cast<T>(SbByteSwapU64(value));
+#elif defined(__clang__) || defined(__GNUC__)
return T(__builtin_bswap64(value));
#elif defined(_MSC_VER)
return T(_byteswap_uint64(value));
diff --git a/src/third_party/mozjs/mfbt/decimal/Decimal.cpp b/src/third_party/mozjs/mfbt/decimal/Decimal.cpp
index de10f83..5af6be1 100644
--- a/src/third_party/mozjs/mfbt/decimal/Decimal.cpp
+++ b/src/third_party/mozjs/mfbt/decimal/Decimal.cpp
@@ -34,6 +34,10 @@
#include <algorithm>
#include <float.h>
+#if defined(STARBOARD)
+#include "starboard/double.h"
+#endif
+
namespace WebCore {
namespace DecimalPrivate {
@@ -684,13 +688,19 @@
Decimal Decimal::fromDouble(double doubleValue)
{
+#if defined(STARBOARD)
+ if (SbDoubleIsFinite(doubleValue))
+ return fromString(mozToString(doubleValue));
+ if (SbDoubleIsNan(doubleValue))
+ return nan();
+ return infinity(doubleValue < 0 ? Negative : Positive);
+#else
if (std::isfinite(doubleValue))
return fromString(mozToString(doubleValue));
-
if (std::isinf(doubleValue))
return infinity(doubleValue < 0 ? Negative : Positive);
-
return nan();
+#endif
}
Decimal Decimal::fromString(const String& str)
diff --git a/src/third_party/mozjs/mozjs.gyp b/src/third_party/mozjs/mozjs.gyp
index 3de7f79..e01f0eb 100644
--- a/src/third_party/mozjs/mozjs.gyp
+++ b/src/third_party/mozjs/mozjs.gyp
@@ -122,6 +122,14 @@
'js-confdefs.h',
],
'conditions': [
+ # These W flags do not work with the ps3 compiler.
+ ['target_arch == "ps3"', {
+ 'cflags!': [
+ '-Wno-invalid-offsetof',
+ '-Wno-uninitialized',
+ '-Wno-unused',
+ ]
+ }],
[ 'target_arch == "x64" and cobalt_enable_jit == 1', {
'sources': [
'js/src/assembler/assembler/MacroAssemblerX86Common.cpp',
@@ -220,6 +228,16 @@
'-include',
'js-confdefs.h',
],
+ 'conditions': [
+ # These W flags do not work with the ps3 compiler.
+ ['target_arch == "ps3"', {
+ 'cflags!': [
+ '-Wno-invalid-offsetof',
+ '-Wno-uninitialized',
+ '-Wno-unused',
+ ]
+ }],
+ ],
},
# Mark this target as a hard dependency because targets that depend on
# this one need to wait for the build_include_directory to be generated.
@@ -365,7 +383,7 @@
'js/src/builtin/embedjs.py',
'-DUSE_ZLIB',
'-p',
- '<(CC) -E',
+ '<(CC_HOST) -E',
'-m',
'js/src/js.msg',
'-o',
diff --git a/src/third_party/openssl/openssl.gyp b/src/third_party/openssl/openssl.gyp
index e84bd8d..484a49e 100644
--- a/src/third_party/openssl/openssl.gyp
+++ b/src/third_party/openssl/openssl.gyp
@@ -225,8 +225,12 @@
'openssl/crypto/des/cfb64enc.c',
'openssl/crypto/des/cfb_enc.c',
'openssl/crypto/des/des_enc.c',
- 'openssl/crypto/des/des_old.c',
- 'openssl/crypto/des/des_old2.c',
+ # All our code uses the uppercase DES_foo() methods, so we don't need
+ # the compatability glue. When linking as a shared library, the unused
+ # glue functions are left as undefined symbols after they get
+ # dead-stripped, so just don't build them.
+ # 'openssl/crypto/des/des_old.c',
+ # 'openssl/crypto/des/des_old2.c',
'openssl/crypto/des/ecb3_enc.c',
'openssl/crypto/des/ecb_enc.c',
'openssl/crypto/des/ede_cbcm_enc.c',