Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f14ebba87 |
@@ -6,3 +6,7 @@
|
|||||||
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
|
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
|
||||||
|
|
||||||
## Release notes for next branch cut
|
## Release notes for next branch cut
|
||||||
|
|
||||||
|
- engine: add `View::getLastDynamicResolutionScale()` (b/457753622)
|
||||||
|
|
||||||
|
- materials: Make Material Instances' UBO descriptor use dynamic offsets. [⚠️ **Recompile Materials**]
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.google.android.filament:filament-android:1.67.1'
|
implementation 'com.google.android.filament:filament-android:1.67.0'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
|
|||||||
iOS projects can use CocoaPods to install the latest release:
|
iOS projects can use CocoaPods to install the latest release:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
pod 'Filament', '~> 1.67.1'
|
pod 'Filament', '~> 1.67.0'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|||||||
@@ -7,12 +7,6 @@ A new header is inserted each time a *tag* is created.
|
|||||||
Instead, if you are authoring a PR for the main branch, add your release note to
|
Instead, if you are authoring a PR for the main branch, add your release note to
|
||||||
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
|
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
|
||||||
|
|
||||||
## v1.68.0
|
|
||||||
|
|
||||||
- engine: add `View::getLastDynamicResolutionScale()` (b/457753622)
|
|
||||||
- Metal: report GPU errors to the platform via `debugUpdateStat` (b/431665753).
|
|
||||||
- materials: Make Material Instances' UBO descriptor use dynamic offsets. [⚠️ **Recompile Materials**]
|
|
||||||
|
|
||||||
## v1.67.1
|
## v1.67.1
|
||||||
|
|
||||||
- Metal: Add support for the `SwapChain::CONFIG_MSAA_4_SAMPLES` flag.
|
- Metal: Add support for the `SwapChain::CONFIG_MSAA_4_SAMPLES` flag.
|
||||||
|
|||||||
-35
@@ -16,9 +16,6 @@
|
|||||||
|
|
||||||
package com.google.android.filament.utils
|
package com.google.android.filament.utils
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.os.Handler
|
|
||||||
import android.os.Looper
|
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.Surface
|
import android.view.Surface
|
||||||
import android.view.SurfaceView
|
import android.view.SurfaceView
|
||||||
@@ -29,7 +26,6 @@ import com.google.android.filament.android.UiHelper
|
|||||||
import com.google.android.filament.gltfio.*
|
import com.google.android.filament.gltfio.*
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import java.nio.Buffer
|
import java.nio.Buffer
|
||||||
import java.nio.ByteBuffer
|
|
||||||
|
|
||||||
private const val kNearPlane = 0.05f // 5 cm
|
private const val kNearPlane = 0.05f // 5 cm
|
||||||
private const val kFarPlane = 1000.0f // 1 km
|
private const val kFarPlane = 1000.0f // 1 km
|
||||||
@@ -123,8 +119,6 @@ class ModelViewer(
|
|||||||
private val target = DoubleArray(3)
|
private val target = DoubleArray(3)
|
||||||
private val upward = DoubleArray(3)
|
private val upward = DoubleArray(3)
|
||||||
|
|
||||||
private var debugFrameCallback: ((Bitmap) -> Unit)? = null
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
renderer = engine.createRenderer()
|
renderer = engine.createRenderer()
|
||||||
scene = engine.createScene()
|
scene = engine.createScene()
|
||||||
@@ -311,39 +305,10 @@ class ModelViewer(
|
|||||||
// Render the scene, unless the renderer wants to skip the frame.
|
// Render the scene, unless the renderer wants to skip the frame.
|
||||||
if (renderer.beginFrame(swapChain!!, frameTimeNanos)) {
|
if (renderer.beginFrame(swapChain!!, frameTimeNanos)) {
|
||||||
renderer.render(view)
|
renderer.render(view)
|
||||||
|
|
||||||
debugFrameCallback?.let {
|
|
||||||
val viewport = view.viewport
|
|
||||||
val bitmap = Bitmap.createBitmap(viewport.width, viewport.height,
|
|
||||||
Bitmap.Config.ARGB_8888)
|
|
||||||
val buffer = ByteBuffer.allocateDirect(viewport.width * viewport.height * 4)
|
|
||||||
|
|
||||||
val handler = Handler(Looper.getMainLooper())
|
|
||||||
val pixelBufferDescriptor = Texture.PixelBufferDescriptor(buffer,
|
|
||||||
Texture.Format.RGBA, Texture.Type.UBYTE, 1, 0, 0, 0, handler) {
|
|
||||||
buffer.rewind()
|
|
||||||
bitmap.copyPixelsFromBuffer(buffer)
|
|
||||||
it(bitmap)
|
|
||||||
}
|
|
||||||
renderer.readPixels(viewport.left, viewport.bottom, viewport.width,
|
|
||||||
viewport.height, pixelBufferDescriptor)
|
|
||||||
debugFrameCallback = null
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer.endFrame()
|
renderer.endFrame()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Sets a callback that will be invoked with the next rendered frame as a Bitmap. Note that this
|
|
||||||
* is a one-time callback.
|
|
||||||
*
|
|
||||||
* @param callback callback to be invoked with a rendered frame as [Bitmap]
|
|
||||||
*/
|
|
||||||
fun debugGetNextFrameCallback(callback: (Bitmap) -> Unit) {
|
|
||||||
debugFrameCallback = callback
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun populateScene(asset: FilamentAsset) {
|
private fun populateScene(asset: FilamentAsset) {
|
||||||
val rcm = engine.renderableManager
|
val rcm = engine.renderableManager
|
||||||
var count = 0
|
var count = 0
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
GROUP=com.google.android.filament
|
GROUP=com.google.android.filament
|
||||||
VERSION_NAME=1.67.1
|
VERSION_NAME=1.67.0
|
||||||
|
|
||||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -181,7 +181,7 @@ important for <code>matc</code> (material compiler).</p>
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.google.android.filament:filament-android:1.67.1'
|
implementation 'com.google.android.filament:filament-android:1.66.2'
|
||||||
}
|
}
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
|
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
|
||||||
@@ -196,7 +196,7 @@ dependencies {
|
|||||||
</div>
|
</div>
|
||||||
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
|
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
|
||||||
<p>iOS projects can use CocoaPods to install the latest release:</p>
|
<p>iOS projects can use CocoaPods to install the latest release:</p>
|
||||||
<pre><code class="language-shell">pod 'Filament', '~> 1.67.1'
|
<pre><code class="language-shell">pod 'Filament', '~> 1.66.2'
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
|
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
|
||||||
<ul>
|
<ul>
|
||||||
|
|||||||
+1788
-1819
File diff suppressed because it is too large
Load Diff
+1174
-1201
File diff suppressed because it is too large
Load Diff
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -19,17 +19,13 @@
|
|||||||
#ifndef TNT_FILAMENT_BACKEND_PLATFORM_H
|
#ifndef TNT_FILAMENT_BACKEND_PLATFORM_H
|
||||||
#define TNT_FILAMENT_BACKEND_PLATFORM_H
|
#define TNT_FILAMENT_BACKEND_PLATFORM_H
|
||||||
|
|
||||||
#include <utils/CString.h>
|
|
||||||
#include <utils/compiler.h>
|
#include <utils/compiler.h>
|
||||||
#include <utils/Invocable.h>
|
#include <utils/Invocable.h>
|
||||||
#include <utils/Mutex.h>
|
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
@@ -512,22 +508,13 @@ public:
|
|||||||
// --------------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------------
|
||||||
// Debugging APIs
|
// Debugging APIs
|
||||||
|
|
||||||
using DebugUpdateStatFunc = utils::Invocable<void(const char* UTILS_NONNULL key,
|
using DebugUpdateStatFunc = utils::Invocable<void(const char* UTILS_NONNULL key, uint64_t value)>;
|
||||||
uint64_t intValue, utils::CString stringValue)>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the callback function that the backend can use to update backend-specific statistics
|
* Sets the callback function that the backend can use to update backend-specific statistics
|
||||||
* to aid with debugging. This callback is guaranteed to be called on the Filament driver
|
* to aid with debugging. This callback is guaranteed to be called on the Filament driver
|
||||||
* thread.
|
* thread.
|
||||||
*
|
*
|
||||||
* The callback signature is (key, intValue, stringValue). Note that for any given call,
|
|
||||||
* only one of the value parameters (intValue or stringValue) will be meaningful, depending on
|
|
||||||
* the specific key.
|
|
||||||
*
|
|
||||||
* IMPORTANT_NOTE: because the callback is called on the driver thread, only quick, non-blocking
|
|
||||||
* work should be done inside it. Furthermore, no graphics API calls (such as GL calls) should
|
|
||||||
* be made, which could interfere with Filament's driver state.
|
|
||||||
*
|
|
||||||
* @param debugUpdateStat an Invocable that updates debug statistics
|
* @param debugUpdateStat an Invocable that updates debug statistics
|
||||||
*/
|
*/
|
||||||
void setDebugUpdateStatFunc(DebugUpdateStatFunc&& debugUpdateStat) noexcept;
|
void setDebugUpdateStatFunc(DebugUpdateStatFunc&& debugUpdateStat) noexcept;
|
||||||
@@ -546,32 +533,15 @@ public:
|
|||||||
* This function is guaranteed to be called only on a single thread, the Filament driver
|
* This function is guaranteed to be called only on a single thread, the Filament driver
|
||||||
* thread.
|
* thread.
|
||||||
*
|
*
|
||||||
* @param key a null-terminated C-string with the key of the debug statistic
|
* @param key a null-terminated C-string with the key of the debug statistic
|
||||||
* @param intValue the updated integer value of key (the string value passed to the
|
* @param value the updated value of key
|
||||||
* callback will be empty)
|
|
||||||
*/
|
*/
|
||||||
void debugUpdateStat(const char* UTILS_NONNULL key, uint64_t intValue);
|
void debugUpdateStat(const char* UTILS_NONNULL key, uint64_t value);
|
||||||
|
|
||||||
/**
|
|
||||||
* To track backend-specific statistics, the backend implementation can call the
|
|
||||||
* application-provided callback function debugUpdateStatFunc to associate or update a value
|
|
||||||
* with a given key. It is possible for this function to be called multiple times with the
|
|
||||||
* same key, in which case newer values should overwrite older values.
|
|
||||||
*
|
|
||||||
* This function is guaranteed to be called only on a single thread, the Filament driver
|
|
||||||
* thread.
|
|
||||||
*
|
|
||||||
* @param key a null-terminated C-string with the key of the debug statistic
|
|
||||||
* @param stringValue the updated string value of key (the integer value passed to the
|
|
||||||
* callback will be 0)
|
|
||||||
*/
|
|
||||||
void debugUpdateStat(const char* UTILS_NONNULL key, utils::CString stringValue);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<InsertBlobFunc> mInsertBlob;
|
InsertBlobFunc mInsertBlob;
|
||||||
std::shared_ptr<RetrieveBlobFunc> mRetrieveBlob;
|
RetrieveBlobFunc mRetrieveBlob;
|
||||||
std::shared_ptr<DebugUpdateStatFunc> mDebugUpdateStat;
|
DebugUpdateStatFunc mDebugUpdateStat;
|
||||||
mutable utils::Mutex mMutex;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace filament
|
} // namespace filament
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_ANDROID_H
|
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_ANDROID_H
|
||||||
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_ANDROID_H
|
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_ANDROID_H
|
||||||
|
|
||||||
|
#include "AndroidSwapChainHelper.h"
|
||||||
|
#include "AndroidFrameCallback.h"
|
||||||
#include "AndroidNdk.h"
|
#include "AndroidNdk.h"
|
||||||
|
|
||||||
#include <backend/AcquiredImage.h>
|
#include <backend/AcquiredImage.h>
|
||||||
@@ -169,10 +171,19 @@ protected:
|
|||||||
SwapChain* drawSwapChain,
|
SwapChain* drawSwapChain,
|
||||||
SwapChain* readSwapChain) override;
|
SwapChain* readSwapChain) override;
|
||||||
|
|
||||||
private:
|
struct SwapChainEGLAndroid : public SwapChainEGL {
|
||||||
struct SwapChainEGLAndroid;
|
SwapChainEGLAndroid(PlatformEGLAndroid const& platform,
|
||||||
struct AndroidDetails;
|
void* nativeWindow, uint64_t flags);
|
||||||
|
SwapChainEGLAndroid(PlatformEGLAndroid const& platform,
|
||||||
|
uint32_t width, uint32_t height, uint64_t flags);
|
||||||
|
void terminate(PlatformEGLAndroid& platform);
|
||||||
|
bool setPresentFrameId(uint64_t frameId) const noexcept;
|
||||||
|
uint64_t getFrameId(uint64_t frameId) const noexcept;
|
||||||
|
private:
|
||||||
|
AndroidSwapChainHelper mImpl{};
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
// prevent derived classes' implementations to call through
|
// prevent derived classes' implementations to call through
|
||||||
[[nodiscard]] SwapChain* createSwapChain(void* nativeWindow, uint64_t flags) override;
|
[[nodiscard]] SwapChain* createSwapChain(void* nativeWindow, uint64_t flags) override;
|
||||||
[[nodiscard]] SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) override;
|
[[nodiscard]] SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) override;
|
||||||
@@ -192,14 +203,17 @@ private:
|
|||||||
|
|
||||||
int mOSVersion;
|
int mOSVersion;
|
||||||
ExternalStreamManagerAndroid& mExternalStreamManager;
|
ExternalStreamManagerAndroid& mExternalStreamManager;
|
||||||
AndroidDetails& mAndroidDetails;
|
|
||||||
InitializeJvmForPerformanceManagerIfNeeded const mInitializeJvmForPerformanceManagerIfNeeded;
|
InitializeJvmForPerformanceManagerIfNeeded const mInitializeJvmForPerformanceManagerIfNeeded;
|
||||||
utils::PerformanceHintManager mPerformanceHintManager;
|
utils::PerformanceHintManager mPerformanceHintManager;
|
||||||
utils::PerformanceHintManager::Session mPerformanceHintSession;
|
utils::PerformanceHintManager::Session mPerformanceHintSession;
|
||||||
|
SwapChainEGLAndroid* mCurrentDrawSwapChain{};
|
||||||
|
|
||||||
using clock = std::chrono::high_resolution_clock;
|
using clock = std::chrono::high_resolution_clock;
|
||||||
clock::time_point mStartTimeOfActualWork;
|
clock::time_point mStartTimeOfActualWork;
|
||||||
SwapChainEGLAndroid* mCurrentDrawSwapChain{};
|
AndroidProducerThrottling mProducerThrottling;
|
||||||
bool mAssertNativeWindowIsValid = false;
|
bool mAssertNativeWindowIsValid = false;
|
||||||
|
|
||||||
|
AndroidFrameCallback mAndroidFrameCallback;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace filament::backend
|
} // namespace filament::backend
|
||||||
|
|||||||
@@ -17,13 +17,12 @@
|
|||||||
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
|
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
|
||||||
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
|
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
|
||||||
|
|
||||||
|
#include "AndroidFrameCallback.h"
|
||||||
#include "AndroidNdk.h"
|
#include "AndroidNdk.h"
|
||||||
|
|
||||||
#include <backend/DriverEnums.h>
|
#include <backend/DriverEnums.h>
|
||||||
#include <backend/platforms/VulkanPlatform.h>
|
#include <backend/platforms/VulkanPlatform.h>
|
||||||
|
|
||||||
#include <utils/compiler.h>
|
|
||||||
|
|
||||||
#include <android/hardware_buffer.h>
|
#include <android/hardware_buffer.h>
|
||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
@@ -92,8 +91,6 @@ protected:
|
|||||||
VkExternalFenceHandleTypeFlagBits getFenceExportFlags() const noexcept override;
|
VkExternalFenceHandleTypeFlagBits getFenceExportFlags() const noexcept override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct AndroidDetails;
|
|
||||||
|
|
||||||
struct ExternalImageVulkanAndroid : public ExternalImage {
|
struct ExternalImageVulkanAndroid : public ExternalImage {
|
||||||
AHardwareBuffer* aHardwareBuffer = nullptr;
|
AHardwareBuffer* aHardwareBuffer = nullptr;
|
||||||
bool sRGB = false;
|
bool sRGB = false;
|
||||||
@@ -102,7 +99,7 @@ private:
|
|||||||
~ExternalImageVulkanAndroid() override;
|
~ExternalImageVulkanAndroid() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
AndroidDetails& mAndroidDetails;
|
AndroidFrameCallback mAndroidFrameCallback;
|
||||||
int mOSVersion{};
|
int mOSVersion{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -61,16 +61,6 @@ int NativeWindow::enableFrameTimestamps(ANativeWindow* anw, bool enable) {
|
|||||||
return pWindow->perform(anw, ENABLE_FRAME_TIMESTAMPS, enable);
|
return pWindow->perform(anw, ENABLE_FRAME_TIMESTAMPS, enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
int NativeWindow::frameTimestampsSupportsPresent(ANativeWindow* anw, bool* outSupportsPresent) {
|
|
||||||
NativeWindow const* pWindow = reinterpret_cast<NativeWindow const*>(anw);
|
|
||||||
int value = 0;
|
|
||||||
bool const success = pWindow->perform(anw, FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value);
|
|
||||||
if (success) {
|
|
||||||
*outSupportsPresent = bool(value);
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
int NativeWindow::getCompositorTiming(ANativeWindow* anw,
|
int NativeWindow::getCompositorTiming(ANativeWindow* anw,
|
||||||
int64_t* compositeDeadline, int64_t* compositeInterval,
|
int64_t* compositeDeadline, int64_t* compositeInterval,
|
||||||
int64_t* compositeToPresentLatency) {
|
int64_t* compositeToPresentLatency) {
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ struct NativeWindow {
|
|||||||
// is valid query enum value
|
// is valid query enum value
|
||||||
enum {
|
enum {
|
||||||
IS_VALID = 17,
|
IS_VALID = 17,
|
||||||
FRAME_TIMESTAMPS_SUPPORTS_PRESENT = 18,
|
|
||||||
GET_NEXT_FRAME_ID = 24,
|
GET_NEXT_FRAME_ID = 24,
|
||||||
ENABLE_FRAME_TIMESTAMPS = 25,
|
ENABLE_FRAME_TIMESTAMPS = 25,
|
||||||
GET_COMPOSITOR_TIMING = 26,
|
GET_COMPOSITOR_TIMING = 26,
|
||||||
@@ -52,7 +51,6 @@ struct NativeWindow {
|
|||||||
|
|
||||||
static int getNextFrameId(ANativeWindow* anw, uint64_t* frameId);
|
static int getNextFrameId(ANativeWindow* anw, uint64_t* frameId);
|
||||||
static int enableFrameTimestamps(ANativeWindow* anw, bool enable);
|
static int enableFrameTimestamps(ANativeWindow* anw, bool enable);
|
||||||
static int frameTimestampsSupportsPresent(ANativeWindow* anw, bool* outSupportsPresent);
|
|
||||||
static int getCompositorTiming(ANativeWindow* anw,
|
static int getCompositorTiming(ANativeWindow* anw,
|
||||||
int64_t* compositeDeadline, int64_t* compositeInterval,
|
int64_t* compositeDeadline, int64_t* compositeInterval,
|
||||||
int64_t* compositeToPresentLatency);
|
int64_t* compositeToPresentLatency);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <backend/platforms/AndroidNdk.h>
|
#include "AndroidNdk.h"
|
||||||
|
|
||||||
#include <android/hardware_buffer.h>
|
#include <android/hardware_buffer.h>
|
||||||
|
|
||||||
|
|||||||
@@ -19,9 +19,6 @@
|
|||||||
|
|
||||||
#include <android/native_window.h>
|
#include <android/native_window.h>
|
||||||
|
|
||||||
#include <utils/compiler.h>
|
|
||||||
#include <utils/Logger.h>
|
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
@@ -39,23 +36,11 @@ bool AndroidSwapChainHelper::setPresentFrameId(
|
|||||||
int const status = NativeWindow::getNextFrameId(anw, &sysFrameId);
|
int const status = NativeWindow::getNextFrameId(anw, &sysFrameId);
|
||||||
if (status == 0) {
|
if (status == 0) {
|
||||||
std::lock_guard const lock(mLock);
|
std::lock_guard const lock(mLock);
|
||||||
// frameIds must be strictly monotonic, if that's not the case (i.e. the new frameId is
|
auto const pos = mFrameIdToSystemFrameId.find(frameId);
|
||||||
// less or equal to the last one in the map), we have to clear the map, because the
|
if (pos && *pos != sysFrameId) {
|
||||||
// map's find() assume sorted keys.
|
// we're trying to associate the same frame id to a different frame!
|
||||||
// This case can happen if two different filament::Renderer are used with the same
|
return false;
|
||||||
// ANativeWindow (the Renderer would have different frameIds). This is expected to
|
|
||||||
// be a rare case.
|
|
||||||
if (UTILS_UNLIKELY(!mFrameIdToSystemFrameId.empty() &&
|
|
||||||
frameId <= mFrameIdToSystemFrameId.back().first)) {
|
|
||||||
// this log is expected to happen very rarely
|
|
||||||
DLOG(INFO) << "clearing frame history anw=" << anw
|
|
||||||
<< ", frameId=" << frameId
|
|
||||||
<< ", previous=" << mFrameIdToSystemFrameId.back().first
|
|
||||||
<< ", sysFrameId=" << sysFrameId;
|
|
||||||
// clear the frame history
|
|
||||||
mFrameIdToSystemFrameId.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// oldest entry is removed
|
// oldest entry is removed
|
||||||
mFrameIdToSystemFrameId.insert(frameId, sysFrameId);
|
mFrameIdToSystemFrameId.insert(frameId, sysFrameId);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -139,73 +139,42 @@ bool Platform::queryFrameTimestamps(SwapChain const*, uint64_t, FrameTimestamps*
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Platform::setBlobFunc(InsertBlobFunc&& insertBlob, RetrieveBlobFunc&& retrieveBlob) noexcept {
|
void Platform::setBlobFunc(InsertBlobFunc&& insertBlob, RetrieveBlobFunc&& retrieveBlob) noexcept {
|
||||||
std::lock_guard<decltype(mMutex)> lock(mMutex);
|
mInsertBlob = std::move(insertBlob);
|
||||||
mInsertBlob = std::make_shared<InsertBlobFunc>(std::move(insertBlob));
|
mRetrieveBlob = std::move(retrieveBlob);
|
||||||
mRetrieveBlob = std::make_shared<RetrieveBlobFunc>(std::move(retrieveBlob));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Platform::hasInsertBlobFunc() const noexcept {
|
bool Platform::hasInsertBlobFunc() const noexcept {
|
||||||
std::lock_guard<decltype(mMutex)> lock(mMutex);
|
return bool(mInsertBlob);
|
||||||
return mInsertBlob && bool(*mInsertBlob);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Platform::hasRetrieveBlobFunc() const noexcept {
|
bool Platform::hasRetrieveBlobFunc() const noexcept {
|
||||||
std::lock_guard<decltype(mMutex)> lock(mMutex);
|
return bool(mRetrieveBlob);
|
||||||
return mRetrieveBlob && bool(*mRetrieveBlob);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Platform::insertBlob(void const* key, size_t keySize, void const* value, size_t valueSize) {
|
void Platform::insertBlob(void const* key, size_t keySize, void const* value, size_t valueSize) {
|
||||||
std::shared_ptr<InsertBlobFunc> callback;
|
if (mInsertBlob) {
|
||||||
{
|
mInsertBlob(key, keySize, value, valueSize);
|
||||||
std::unique_lock<decltype(mMutex)> lock(mMutex);
|
|
||||||
callback = mInsertBlob;
|
|
||||||
}
|
|
||||||
if (callback) {
|
|
||||||
(*callback)(key, keySize, value, valueSize);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Platform::retrieveBlob(void const* key, size_t keySize, void* value, size_t valueSize) {
|
size_t Platform::retrieveBlob(void const* key, size_t keySize, void* value, size_t valueSize) {
|
||||||
std::shared_ptr<RetrieveBlobFunc> callback;
|
if (mRetrieveBlob) {
|
||||||
{
|
return mRetrieveBlob(key, keySize, value, valueSize);
|
||||||
std::unique_lock<decltype(mMutex)> lock(mMutex);
|
|
||||||
callback = mRetrieveBlob;
|
|
||||||
}
|
|
||||||
if (callback) {
|
|
||||||
return (*callback)(key, keySize, value, valueSize);
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Platform::setDebugUpdateStatFunc(DebugUpdateStatFunc&& debugUpdateStat) noexcept {
|
void Platform::setDebugUpdateStatFunc(DebugUpdateStatFunc&& debugUpdateStat) noexcept {
|
||||||
std::lock_guard<decltype(mMutex)> lock(mMutex);
|
mDebugUpdateStat = std::move(debugUpdateStat);
|
||||||
mDebugUpdateStat = std::make_shared<DebugUpdateStatFunc>(std::move(debugUpdateStat));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Platform::hasDebugUpdateStatFunc() const noexcept {
|
bool Platform::hasDebugUpdateStatFunc() const noexcept {
|
||||||
std::lock_guard<decltype(mMutex)> lock(mMutex);
|
return bool(mDebugUpdateStat);
|
||||||
return mDebugUpdateStat && bool(*mDebugUpdateStat);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Platform::debugUpdateStat(const char* key, uint64_t intValue) {
|
void Platform::debugUpdateStat(const char* key, uint64_t value) {
|
||||||
std::shared_ptr<DebugUpdateStatFunc> callback;
|
if (mDebugUpdateStat) {
|
||||||
{
|
mDebugUpdateStat(key, value);
|
||||||
std::unique_lock<decltype(mMutex)> lock(mMutex);
|
|
||||||
callback = mDebugUpdateStat;
|
|
||||||
}
|
|
||||||
if (callback) {
|
|
||||||
(*callback)(key, intValue, "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Platform::debugUpdateStat(const char* key, utils::CString stringValue) {
|
|
||||||
std::shared_ptr<DebugUpdateStatFunc> callback;
|
|
||||||
{
|
|
||||||
std::unique_lock<decltype(mMutex)> lock(mMutex);
|
|
||||||
callback = mDebugUpdateStat;
|
|
||||||
}
|
|
||||||
if (callback) {
|
|
||||||
(*callback)(key, 0, stringValue);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ public:
|
|||||||
size_t size, bool forceGpuBuffer = false);
|
size_t size, bool forceGpuBuffer = false);
|
||||||
~MetalBuffer();
|
~MetalBuffer();
|
||||||
|
|
||||||
[[nodiscard]] bool wasAllocationSuccessful() const noexcept { return mBuffer; }
|
[[nodiscard]] bool wasAllocationSuccessful() const noexcept { return mBuffer || mCpuBuffer; }
|
||||||
|
|
||||||
MetalBuffer(const MetalBuffer& rhs) = delete;
|
MetalBuffer(const MetalBuffer& rhs) = delete;
|
||||||
MetalBuffer& operator=(const MetalBuffer& rhs) = delete;
|
MetalBuffer& operator=(const MetalBuffer& rhs) = delete;
|
||||||
@@ -185,12 +185,14 @@ public:
|
|||||||
* Denotes that this buffer is used for a draw call ensuring that its allocation remains valid
|
* Denotes that this buffer is used for a draw call ensuring that its allocation remains valid
|
||||||
* until the end of the current frame.
|
* until the end of the current frame.
|
||||||
*
|
*
|
||||||
* @return The MTLBuffer representing the current state of the buffer to bind, it never returns
|
* @return The MTLBuffer representing the current state of the buffer to bind, or nil if there
|
||||||
* nil.
|
* is no device allocation.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
id<MTLBuffer> getGpuBufferForDraw() noexcept;
|
id<MTLBuffer> getGpuBufferForDraw() noexcept;
|
||||||
|
|
||||||
|
void* getCpuBuffer() const noexcept { return mCpuBuffer; }
|
||||||
|
|
||||||
void setLabel(const utils::ImmutableCString& label) {
|
void setLabel(const utils::ImmutableCString& label) {
|
||||||
#if FILAMENT_METAL_DEBUG_LABELS
|
#if FILAMENT_METAL_DEBUG_LABELS
|
||||||
if (label.empty()) {
|
if (label.empty()) {
|
||||||
@@ -233,6 +235,7 @@ private:
|
|||||||
UploadStrategy mUploadStrategy;
|
UploadStrategy mUploadStrategy;
|
||||||
TrackedMetalBuffer mBuffer;
|
TrackedMetalBuffer mBuffer;
|
||||||
size_t mBufferSize = 0;
|
size_t mBufferSize = 0;
|
||||||
|
void* mCpuBuffer = nullptr;
|
||||||
MetalContext& mContext;
|
MetalContext& mContext;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -39,30 +39,34 @@ MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType,
|
|||||||
mUploadStrategy = UploadStrategy::POOL;
|
mUploadStrategy = UploadStrategy::POOL;
|
||||||
}
|
}
|
||||||
|
|
||||||
MTLResourceOptions options = MTLResourceStorageModePrivate;
|
// If the buffer is less than 4K in size and is updated frequently, we don't use an explicit
|
||||||
|
// buffer. Instead, we use immediate command encoder methods like setVertexBytes:length:atIndex:.
|
||||||
|
// This won't work for SSBOs, since they are read/write.
|
||||||
|
|
||||||
// The buffer will be memory mapped for write operations.
|
/*
|
||||||
if (any(usage & BufferUsage::SHARED_WRITE_BIT)) {
|
if (size <= 4 * 1024 && bindingType != BufferObjectBinding::SHADER_STORAGE &&
|
||||||
#if defined(FILAMENT_IOS) || defined(__arm64__) || defined(__aarch64__)
|
usage == BufferUsage::DYNAMIC && !forceGpuBuffer) {
|
||||||
// iOS and Apple Silicon devices use UMA (Unified Memory Architecture), so we use Shared memory.
|
mBuffer = nil;
|
||||||
options = MTLResourceStorageModeShared;
|
mCpuBuffer = malloc(size);
|
||||||
#else
|
return;
|
||||||
// Intel Macs require Managed memory for CPU/GPU synchronization.
|
|
||||||
options = MTLResourceStorageModeManaged;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Otherwise, we allocate a private GPU buffer.
|
||||||
{
|
{
|
||||||
ScopedAllocationTimer timer("generic");
|
ScopedAllocationTimer timer("generic");
|
||||||
mBuffer = { [context.device newBufferWithLength:size options:options],
|
mBuffer = { [context.device newBufferWithLength:size options:MTLResourceStorageModePrivate],
|
||||||
TrackedMetalBuffer::Type::GENERIC };
|
TrackedMetalBuffer::Type::GENERIC };
|
||||||
}
|
}
|
||||||
|
|
||||||
// mBuffer might fail to be allocated. Clients can check for this by calling
|
// mBuffer might fail to be allocated. Clients can check for this by calling
|
||||||
// wasAllocationSuccessful().
|
// wasAllocationSuccessful().
|
||||||
}
|
}
|
||||||
|
|
||||||
MetalBuffer::~MetalBuffer() = default;
|
MetalBuffer::~MetalBuffer() {
|
||||||
|
if (mCpuBuffer) {
|
||||||
|
free(mCpuBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MetalBuffer::copyIntoBuffer(
|
void MetalBuffer::copyIntoBuffer(
|
||||||
void* src, size_t size, size_t byteOffset, TagResolver&& getHandleTag) {
|
void* src, size_t size, size_t byteOffset, TagResolver&& getHandleTag) {
|
||||||
@@ -79,6 +83,12 @@ void MetalBuffer::copyIntoBuffer(
|
|||||||
FILAMENT_CHECK_PRECONDITION(!(byteOffset & 0x3))
|
FILAMENT_CHECK_PRECONDITION(!(byteOffset & 0x3))
|
||||||
<< "byteOffset must be a multiple of 4, tag=" << getHandleTag();
|
<< "byteOffset must be a multiple of 4, tag=" << getHandleTag();
|
||||||
|
|
||||||
|
// If we have a cpu buffer, we can directly copy into it.
|
||||||
|
if (mCpuBuffer) {
|
||||||
|
memcpy(static_cast<uint8_t*>(mCpuBuffer) + byteOffset, src, size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (mUploadStrategy) {
|
switch (mUploadStrategy) {
|
||||||
case UploadStrategy::BUMP_ALLOCATOR:
|
case UploadStrategy::BUMP_ALLOCATOR:
|
||||||
uploadWithBumpAllocator(src, size, byteOffset, std::move(getHandleTag));
|
uploadWithBumpAllocator(src, size, byteOffset, std::move(getHandleTag));
|
||||||
@@ -96,6 +106,11 @@ void MetalBuffer::copyIntoBufferUnsynchronized(
|
|||||||
}
|
}
|
||||||
|
|
||||||
id<MTLBuffer> MetalBuffer::getGpuBufferForDraw() noexcept {
|
id<MTLBuffer> MetalBuffer::getGpuBufferForDraw() noexcept {
|
||||||
|
// If there's a CPU buffer, then we return nil here, as the CPU-side buffer will be bound
|
||||||
|
// separately.
|
||||||
|
if (mCpuBuffer) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
assert_invariant(mBuffer);
|
assert_invariant(mBuffer);
|
||||||
return mBuffer.get();
|
return mBuffer.get();
|
||||||
}
|
}
|
||||||
@@ -156,6 +171,41 @@ void MetalBuffer::bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLCommandEncod
|
|||||||
offsets:metalOffsets.data()
|
offsets:metalOffsets.data()
|
||||||
withRange:bufferRange];
|
withRange:bufferRange];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (size_t b = 0; b < count; b++) {
|
||||||
|
MetalBuffer* const buffer = buffers[b];
|
||||||
|
if (!buffer) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const void* cpuBuffer = buffer->getCpuBuffer();
|
||||||
|
if (!cpuBuffer) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t bufferIndex = bufferStart + b;
|
||||||
|
const size_t offset = offsets[b];
|
||||||
|
auto* bytes = static_cast<const uint8_t*>(cpuBuffer);
|
||||||
|
|
||||||
|
if (stages & Stage::VERTEX) {
|
||||||
|
[(id<MTLRenderCommandEncoder>) encoder setVertexBytes:(bytes + offset)
|
||||||
|
length:(buffer->getSize() - offset)
|
||||||
|
atIndex:bufferIndex];
|
||||||
|
}
|
||||||
|
if (stages & Stage::FRAGMENT) {
|
||||||
|
[(id<MTLRenderCommandEncoder>) encoder setFragmentBytes:(bytes + offset)
|
||||||
|
length:(buffer->getSize() - offset)
|
||||||
|
atIndex:bufferIndex];
|
||||||
|
}
|
||||||
|
if (stages & Stage::COMPUTE) {
|
||||||
|
// TODO: using setBytes means the data is read-only, which currently isn't enforced.
|
||||||
|
// In practice this won't be an issue since MetalBuffer ensures all SSBOs are realized
|
||||||
|
// through actual id<MTLBuffer> allocations.
|
||||||
|
[(id<MTLComputeCommandEncoder>) encoder setBytes:(bytes + offset)
|
||||||
|
length:(buffer->getSize() - offset)
|
||||||
|
atIndex:bufferIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MetalBuffer::uploadWithPoolBuffer(
|
void MetalBuffer::uploadWithPoolBuffer(
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
#ifndef TNT_METALCONTEXT_H
|
#ifndef TNT_METALCONTEXT_H
|
||||||
#define TNT_METALCONTEXT_H
|
#define TNT_METALCONTEXT_H
|
||||||
|
|
||||||
#include "MetalErrorQueue.h"
|
|
||||||
#include "MetalResourceTracker.h"
|
#include "MetalResourceTracker.h"
|
||||||
#include "MetalShaderCompiler.h"
|
#include "MetalShaderCompiler.h"
|
||||||
#include "MetalState.h"
|
#include "MetalState.h"
|
||||||
@@ -130,7 +129,6 @@ struct MetalContext {
|
|||||||
id<MTLCommandBuffer> pendingCommandBuffer = nil;
|
id<MTLCommandBuffer> pendingCommandBuffer = nil;
|
||||||
id<MTLRenderCommandEncoder> currentRenderPassEncoder = nil;
|
id<MTLRenderCommandEncoder> currentRenderPassEncoder = nil;
|
||||||
uint32_t currentFrame = 0;
|
uint32_t currentFrame = 0;
|
||||||
MetalErrorQueue commandBufferErrors;
|
|
||||||
|
|
||||||
std::atomic<bool> memorylessLimitsReached = false;
|
std::atomic<bool> memorylessLimitsReached = false;
|
||||||
|
|
||||||
|
|||||||
@@ -153,7 +153,6 @@ id<MTLCommandBuffer> getPendingCommandBuffer(MetalContext* context) {
|
|||||||
|
|
||||||
if (UTILS_UNLIKELY(errorCode != MTLCommandBufferErrorNone)) {
|
if (UTILS_UNLIKELY(errorCode != MTLCommandBufferErrorNone)) {
|
||||||
logMTLCommandBufferError(errorCode);
|
logMTLCommandBufferError(errorCode);
|
||||||
context->commandBufferErrors.push(buffer.error);
|
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
FILAMENT_CHECK_POSTCONDITION(context->pendingCommandBuffer)
|
FILAMENT_CHECK_POSTCONDITION(context->pendingCommandBuffer)
|
||||||
|
|||||||
@@ -61,8 +61,6 @@ public:
|
|||||||
|
|
||||||
MetalContext* getContext() { return mContext; }
|
MetalContext* getContext() { return mContext; }
|
||||||
|
|
||||||
using DriverBase::scheduleDestroy;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
friend class MetalSwapChain;
|
friend class MetalSwapChain;
|
||||||
|
|||||||
@@ -254,18 +254,6 @@ MetalDriver::~MetalDriver() noexcept {
|
|||||||
void MetalDriver::tick(int) {
|
void MetalDriver::tick(int) {
|
||||||
executeTickOps();
|
executeTickOps();
|
||||||
executeDeferredOps();
|
executeDeferredOps();
|
||||||
|
|
||||||
// Notify platform of GPU errors.
|
|
||||||
auto& platform = mPlatform;
|
|
||||||
if (UTILS_UNLIKELY(!mContext->commandBufferErrors.isEmpty())) {
|
|
||||||
mContext->commandBufferErrors.flush([&platform](NSError* error) {
|
|
||||||
if (UTILS_VERY_UNLIKELY(!error)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const utils::CString errorString(error.localizedDescription.UTF8String);
|
|
||||||
platform.debugUpdateStat("filament.metal.command_buffer_error", errorString);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MetalDriver::beginFrame(int64_t monotonic_clock_ns,
|
void MetalDriver::beginFrame(int64_t monotonic_clock_ns,
|
||||||
@@ -2275,10 +2263,7 @@ MemoryMappedBufferHandle MetalDriver::mapBufferS() noexcept {
|
|||||||
void MetalDriver::mapBufferR(MemoryMappedBufferHandle mmbh,
|
void MetalDriver::mapBufferR(MemoryMappedBufferHandle mmbh,
|
||||||
BufferObjectHandle boh, size_t offset,
|
BufferObjectHandle boh, size_t offset,
|
||||||
size_t size, MapBufferAccessFlags access, utils::ImmutableCString&& tag) {
|
size_t size, MapBufferAccessFlags access, utils::ImmutableCString&& tag) {
|
||||||
assert_invariant(boh);
|
construct_handle<MetalMemoryMappedBuffer>(mmbh, boh, offset, size, access);
|
||||||
MetalBufferObject* bo = mHandleAllocator.handle_cast<MetalBufferObject*>(boh);
|
|
||||||
assert_invariant(bo);
|
|
||||||
construct_handle<MetalMemoryMappedBuffer>(mmbh, bo, offset, size, access);
|
|
||||||
mHandleAllocator.associateTagToHandle(mmbh.getId(), std::move(tag));
|
mHandleAllocator.associateTagToHandle(mmbh.getId(), std::move(tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2286,16 +2271,21 @@ void MetalDriver::unmapBuffer(MemoryMappedBufferHandle mmbh) {
|
|||||||
if (UTILS_UNLIKELY(!mmbh)) {
|
if (UTILS_UNLIKELY(!mmbh)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* mmb = handle_cast<MetalMemoryMappedBuffer>(mmbh);
|
|
||||||
mmb->unmap();
|
|
||||||
destruct_handle<MetalMemoryMappedBuffer>(mmbh);
|
destruct_handle<MetalMemoryMappedBuffer>(mmbh);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MetalDriver::copyToMemoryMappedBuffer(MemoryMappedBufferHandle mmbh, size_t offset,
|
void MetalDriver::copyToMemoryMappedBuffer(MemoryMappedBufferHandle mmbh, size_t offset,
|
||||||
BufferDescriptor&& data) {
|
BufferDescriptor&& data) {
|
||||||
auto* mmb = handle_cast<MetalMemoryMappedBuffer>(mmbh);
|
auto mmb = handle_cast<MetalMemoryMappedBuffer>(mmbh);
|
||||||
mmb->copy(*this, offset, std::move(data));
|
|
||||||
|
assert_invariant(any(mmb->access & MapBufferAccessFlags::WRITE_BIT));
|
||||||
|
assert_invariant(offset + data.size <= mmb->size);
|
||||||
|
|
||||||
|
// TODO: this isa zero-effort implementation of copyToMemoryMappedBuffer(), where we just
|
||||||
|
// call updateBufferObject(). This could be a fallback implementation for when
|
||||||
|
// shared memory is not available.
|
||||||
|
// On UMA systems, this should just be a memcpy into the memory-mapped buffer.
|
||||||
|
updateBufferObject(mmb->boh, std::move(data), mmb->offset + offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
// explicit instantiation of the Dispatcher
|
// explicit instantiation of the Dispatcher
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2025 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* 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 TNT_FILAMENT_DRIVER_METALERRORQUEUE_H
|
|
||||||
#define TNT_FILAMENT_DRIVER_METALERRORQUEUE_H
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
#include <utils/compiler.h>
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <functional>
|
|
||||||
#include <mutex>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
class MetalErrorQueue {
|
|
||||||
public:
|
|
||||||
bool isEmpty() const {
|
|
||||||
return !mHasErrors.load(std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void push(NSError* error) {
|
|
||||||
std::lock_guard<std::mutex> lock(mMutex);
|
|
||||||
mErrors.push_back(error);
|
|
||||||
mHasErrors.store(true, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush(const std::function<void(NSError*)>& callback) {
|
|
||||||
if (UTILS_LIKELY(isEmpty())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::vector<NSError*> errors;
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mMutex);
|
|
||||||
std::swap(mErrors, errors);
|
|
||||||
mHasErrors.store(false, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& error: errors) {
|
|
||||||
callback(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<NSError*> mErrors;
|
|
||||||
std::mutex mMutex;
|
|
||||||
|
|
||||||
// Optimization to avoid locking the mutex at each call to flush.
|
|
||||||
std::atomic<bool> mHasErrors;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif // TNT_FILAMENT_DRIVER_METALERRORQUEUE_H
|
|
||||||
@@ -569,22 +569,14 @@ struct MetalDescriptorSet : public HwDescriptorSet {
|
|||||||
|
|
||||||
|
|
||||||
struct MetalMemoryMappedBuffer : public HwMemoryMappedBuffer {
|
struct MetalMemoryMappedBuffer : public HwMemoryMappedBuffer {
|
||||||
|
MetalMemoryMappedBuffer(BufferObjectHandle boh, size_t const offset,
|
||||||
|
size_t const size, MapBufferAccessFlags const access)
|
||||||
|
: boh(boh), access(access), size(size), offset(offset) {
|
||||||
|
}
|
||||||
|
BufferObjectHandle boh{};
|
||||||
MapBufferAccessFlags access{};
|
MapBufferAccessFlags access{};
|
||||||
struct {
|
uint32_t size = 0;
|
||||||
MetalBufferObject* bo;
|
uint32_t offset = 0;
|
||||||
void* vaddr = nullptr;
|
|
||||||
uint32_t size = 0;
|
|
||||||
uint32_t offset = 0;
|
|
||||||
} mtl;
|
|
||||||
|
|
||||||
MetalMemoryMappedBuffer(MetalBufferObject* bo, size_t offset, size_t size,
|
|
||||||
MapBufferAccessFlags access) noexcept;
|
|
||||||
|
|
||||||
~MetalMemoryMappedBuffer();
|
|
||||||
|
|
||||||
void unmap();
|
|
||||||
|
|
||||||
void copy(MetalDriver& mtld, size_t offset, BufferDescriptor&& data) const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace backend
|
} // namespace backend
|
||||||
|
|||||||
@@ -1664,44 +1664,5 @@ id<MTLBuffer> MetalDescriptorSet::finalizeAndGetBuffer(MetalDriver* driver, Shad
|
|||||||
return buffer.get();
|
return buffer.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
MetalMemoryMappedBuffer::MetalMemoryMappedBuffer(MetalBufferObject* bo, size_t offset, size_t size,
|
|
||||||
MapBufferAccessFlags access) noexcept : access(access) {
|
|
||||||
MetalBuffer* buffer = bo->getBuffer();
|
|
||||||
assert_invariant(buffer);
|
|
||||||
id<MTLBuffer> mtlBuffer = buffer->getGpuBufferForDraw();
|
|
||||||
|
|
||||||
assert_invariant(offset + size <= bo->byteCount);
|
|
||||||
assert_invariant(mtlBuffer.storageMode != MTLStorageModePrivate);
|
|
||||||
|
|
||||||
mtl.bo = bo;
|
|
||||||
mtl.vaddr = static_cast<char*>(mtlBuffer.contents) + offset;
|
|
||||||
mtl.size = size;
|
|
||||||
mtl.offset = offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
MetalMemoryMappedBuffer::~MetalMemoryMappedBuffer() = default;
|
|
||||||
|
|
||||||
void MetalMemoryMappedBuffer::unmap() {
|
|
||||||
#if !defined(FILAMENT_IOS) && defined(__x86_64__)
|
|
||||||
// Managed memory requires didModifyRange to synchronize changes to the GPU. This is specific to Intel Macs.
|
|
||||||
MetalBuffer* buffer = bo->getBuffer();
|
|
||||||
id<MTLBuffer> mtlBuffer = buffer->getGpuBufferForDraw();
|
|
||||||
if (mtlBuffer && mtlBuffer.storageMode == MTLStorageModeManaged) {
|
|
||||||
[mtlBuffer didModifyRange:NSMakeRange(mtl.offset, mtl.size)];
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// Shared memory on UMA systems is coherent; no explicit synchronization is required.
|
|
||||||
}
|
|
||||||
|
|
||||||
void MetalMemoryMappedBuffer::copy(MetalDriver& mtld, size_t offset, BufferDescriptor&& data) const {
|
|
||||||
assert_invariant(any(access & MapBufferAccessFlags::WRITE_BIT));
|
|
||||||
assert_invariant(offset + data.size <= mtl.size);
|
|
||||||
assert_invariant(mtl.vaddr);
|
|
||||||
|
|
||||||
memcpy(static_cast<char*>(mtl.vaddr) + offset, data.buffer, data.size);
|
|
||||||
|
|
||||||
mtld.scheduleDestroy(std::move(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace backend
|
} // namespace backend
|
||||||
} // namespace filament
|
} // namespace filament
|
||||||
|
|||||||
@@ -28,13 +28,14 @@
|
|||||||
#include <private/backend/VirtualMachineEnv.h>
|
#include <private/backend/VirtualMachineEnv.h>
|
||||||
|
|
||||||
#include "AndroidNativeWindow.h"
|
#include "AndroidNativeWindow.h"
|
||||||
#include "AndroidFrameCallback.h"
|
|
||||||
#include "AndroidSwapChainHelper.h"
|
#include "AndroidSwapChainHelper.h"
|
||||||
#include "ExternalStreamManagerAndroid.h"
|
#include "ExternalStreamManagerAndroid.h"
|
||||||
|
|
||||||
#include <android/api-level.h>
|
#include <android/api-level.h>
|
||||||
#include <android/native_window.h>
|
#include <android/native_window.h>
|
||||||
#include <android/hardware_buffer.h>
|
#include <android/hardware_buffer.h>
|
||||||
|
#include <android/choreographer.h>
|
||||||
|
#include <android/looper.h>
|
||||||
|
|
||||||
#include <utils/android/PerformanceHintManager.h>
|
#include <utils/android/PerformanceHintManager.h>
|
||||||
#include <utils/compiler.h>
|
#include <utils/compiler.h>
|
||||||
@@ -98,27 +99,6 @@ using namespace glext;
|
|||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
struct PlatformEGLAndroid::SwapChainEGLAndroid : public SwapChainEGL {
|
|
||||||
SwapChainEGLAndroid(PlatformEGLAndroid const& platform,
|
|
||||||
void* nativeWindow, uint64_t flags);
|
|
||||||
SwapChainEGLAndroid(PlatformEGLAndroid const& platform,
|
|
||||||
uint32_t width, uint32_t height, uint64_t flags);
|
|
||||||
void terminate(PlatformEGLAndroid& platform);
|
|
||||||
bool setPresentFrameId(uint64_t frameId) const noexcept;
|
|
||||||
uint64_t getFrameId(uint64_t frameId) const noexcept;
|
|
||||||
bool compositorTimingSupported = false;
|
|
||||||
bool frameTimestampsSupported = false;
|
|
||||||
private:
|
|
||||||
AndroidSwapChainHelper mImpl{};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PlatformEGLAndroid::AndroidDetails {
|
|
||||||
AndroidProducerThrottling producerThrottling;
|
|
||||||
AndroidFrameCallback androidFrameCallback;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
PlatformEGLAndroid::InitializeJvmForPerformanceManagerIfNeeded::InitializeJvmForPerformanceManagerIfNeeded() {
|
PlatformEGLAndroid::InitializeJvmForPerformanceManagerIfNeeded::InitializeJvmForPerformanceManagerIfNeeded() {
|
||||||
// PerformanceHintManager() needs the calling thread to be a Java thread; so we need
|
// PerformanceHintManager() needs the calling thread to be a Java thread; so we need
|
||||||
// to attach this thread to the JVM before we initialize PerformanceHintManager.
|
// to attach this thread to the JVM before we initialize PerformanceHintManager.
|
||||||
@@ -132,20 +112,17 @@ PlatformEGLAndroid::InitializeJvmForPerformanceManagerIfNeeded::InitializeJvmFor
|
|||||||
// ---------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
PlatformEGLAndroid::PlatformEGLAndroid() noexcept
|
PlatformEGLAndroid::PlatformEGLAndroid() noexcept
|
||||||
: mExternalStreamManager(ExternalStreamManagerAndroid::create()),
|
: mExternalStreamManager(ExternalStreamManagerAndroid::create()) {
|
||||||
mAndroidDetails(*(new(std::nothrow) AndroidDetails{})) {
|
|
||||||
mOSVersion = android_get_device_api_level();
|
mOSVersion = android_get_device_api_level();
|
||||||
if (mOSVersion < 0) {
|
if (mOSVersion < 0) {
|
||||||
mOSVersion = __ANDROID_API_FUTURE__;
|
mOSVersion = __ANDROID_API_FUTURE__;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PlatformEGLAndroid::~PlatformEGLAndroid() noexcept {
|
PlatformEGLAndroid::~PlatformEGLAndroid() noexcept = default;
|
||||||
delete &mAndroidDetails;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlatformEGLAndroid::terminate() noexcept {
|
void PlatformEGLAndroid::terminate() noexcept {
|
||||||
mAndroidDetails.androidFrameCallback.terminate();
|
mAndroidFrameCallback.terminate();
|
||||||
ExternalStreamManagerAndroid::destroy(&mExternalStreamManager);
|
ExternalStreamManagerAndroid::destroy(&mExternalStreamManager);
|
||||||
PlatformEGL::terminate();
|
PlatformEGL::terminate();
|
||||||
}
|
}
|
||||||
@@ -230,9 +207,9 @@ Driver* PlatformEGLAndroid::createDriver(void* sharedContext,
|
|||||||
"eglGetNativeClientBufferANDROID"));
|
"eglGetNativeClientBufferANDROID"));
|
||||||
|
|
||||||
if (ext.egl.ANDROID_presentation_time) {
|
if (ext.egl.ANDROID_presentation_time) {
|
||||||
eglPresentationTimeANDROID =
|
eglGetNativeClientBufferANDROID =
|
||||||
PFNEGLPRESENTATIONTIMEANDROIDPROC(eglGetProcAddress(
|
PFNEGLGETNATIVECLIENTBUFFERANDROIDPROC(eglGetProcAddress(
|
||||||
"eglPresentationTimeANDROID"));
|
"eglGetNativeClientBufferANDROID"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ext.egl.ANDROID_get_frame_timestamps) {
|
if (ext.egl.ANDROID_get_frame_timestamps) {
|
||||||
@@ -263,7 +240,7 @@ Driver* PlatformEGLAndroid::createDriver(void* sharedContext,
|
|||||||
|
|
||||||
mAssertNativeWindowIsValid = driverConfig.assertNativeWindowIsValid;
|
mAssertNativeWindowIsValid = driverConfig.assertNativeWindowIsValid;
|
||||||
|
|
||||||
mAndroidDetails.androidFrameCallback.init();
|
mAndroidFrameCallback.init();
|
||||||
|
|
||||||
return driver;
|
return driver;
|
||||||
}
|
}
|
||||||
@@ -287,25 +264,15 @@ bool PlatformEGLAndroid::queryCompositorTiming(SwapChain const* swapchain,
|
|||||||
}
|
}
|
||||||
|
|
||||||
AndroidFrameCallback::Timeline const preferredTimeline{
|
AndroidFrameCallback::Timeline const preferredTimeline{
|
||||||
mAndroidDetails.androidFrameCallback.getPreferredTimeline() };
|
mAndroidFrameCallback.getPreferredTimeline() };
|
||||||
outCompositorTiming->frameTime = preferredTimeline.frameTime;
|
outCompositorTiming->frameTime = preferredTimeline.frameTime;
|
||||||
outCompositorTiming->expectedPresentTime = preferredTimeline.expectedPresentTime;
|
outCompositorTiming->expectedPresentTime = preferredTimeline.expectedPresentTime;
|
||||||
outCompositorTiming->frameTimelineDeadline = preferredTimeline.frameTimelineDeadline;
|
outCompositorTiming->frameTimelineDeadline = preferredTimeline.frameTimelineDeadline;
|
||||||
outCompositorTiming->compositeDeadline = CompositorTiming::INVALID;
|
|
||||||
outCompositorTiming->compositeInterval = CompositorTiming::INVALID;
|
|
||||||
outCompositorTiming->compositeToPresentLatency = CompositorTiming::INVALID;
|
|
||||||
|
|
||||||
// From this point on, we always return "success" because some timings were returned.
|
|
||||||
|
|
||||||
if (!static_cast<SwapChainEGLAndroid const *>(swapchain)->compositorTimingSupported) {
|
|
||||||
// if this surface doesn't support it, don't attempt to query the values.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (UTILS_LIKELY(ext.egl.ANDROID_get_frame_timestamps)) {
|
if (UTILS_LIKELY(ext.egl.ANDROID_get_frame_timestamps)) {
|
||||||
EGLSurface const sur = static_cast<SwapChainEGL const *>(swapchain)->sur;
|
EGLSurface const sur = static_cast<SwapChainEGL const *>(swapchain)->sur;
|
||||||
if (sur == EGL_NO_SURFACE) {
|
if (sur == EGL_NO_SURFACE) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<EGLnsecsANDROID, 3> values;
|
std::array<EGLnsecsANDROID, 3> values;
|
||||||
@@ -316,16 +283,26 @@ bool PlatformEGLAndroid::queryCompositorTiming(SwapChain const* swapchain,
|
|||||||
};
|
};
|
||||||
EGLBoolean const success = eglGetCompositorTimingANDROID(getEglDisplay(), sur,
|
EGLBoolean const success = eglGetCompositorTimingANDROID(getEglDisplay(), sur,
|
||||||
names.size(), names.data(), values.data());
|
names.size(), names.data(), values.data());
|
||||||
if (UTILS_UNLIKELY(!success)) {
|
if (!success) {
|
||||||
// reset current error to EGL_SUCCESS
|
return false;
|
||||||
eglGetError();
|
|
||||||
} else {
|
|
||||||
outCompositorTiming->compositeDeadline = values[0];
|
|
||||||
outCompositorTiming->compositeInterval = values[1];
|
|
||||||
outCompositorTiming->compositeToPresentLatency = values[2];
|
|
||||||
}
|
}
|
||||||
|
outCompositorTiming->compositeDeadline = values[0];
|
||||||
|
outCompositorTiming->compositeInterval = values[1];
|
||||||
|
outCompositorTiming->compositeToPresentLatency = values[2];
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
|
// fallback to private APIs
|
||||||
|
auto const anw = static_cast<SwapChainEGL const *>(swapchain)->nativeWindow;
|
||||||
|
int const status = NativeWindow::getCompositorTiming(anw,
|
||||||
|
&outCompositorTiming->compositeDeadline,
|
||||||
|
&outCompositorTiming->compositeInterval,
|
||||||
|
&outCompositorTiming->compositeToPresentLatency);
|
||||||
|
if (status == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PlatformEGL::queryCompositorTiming(swapchain, outCompositorTiming);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PlatformEGLAndroid::setPresentFrameId(SwapChain const* swapchain,
|
bool PlatformEGLAndroid::setPresentFrameId(SwapChain const* swapchain,
|
||||||
@@ -350,10 +327,6 @@ bool PlatformEGLAndroid::queryFrameTimestamps(SwapChain const* swapchain, uint64
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!static_cast<SwapChainEGLAndroid const *>(swapchain)->frameTimestampsSupported) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (UTILS_LIKELY(ext.egl.ANDROID_get_frame_timestamps)) {
|
if (UTILS_LIKELY(ext.egl.ANDROID_get_frame_timestamps)) {
|
||||||
EGLSurface const sur = sc->sur;
|
EGLSurface const sur = sc->sur;
|
||||||
if (sur == EGL_NO_SURFACE) {
|
if (sur == EGL_NO_SURFACE) {
|
||||||
@@ -374,9 +347,7 @@ bool PlatformEGLAndroid::queryFrameTimestamps(SwapChain const* swapchain, uint64
|
|||||||
};
|
};
|
||||||
EGLBoolean const success = eglGetFrameTimestampsANDROID(getEglDisplay(), sur, hwFrameId,
|
EGLBoolean const success = eglGetFrameTimestampsANDROID(getEglDisplay(), sur, hwFrameId,
|
||||||
names.size(), names.data(), values.data());
|
names.size(), names.data(), values.data());
|
||||||
if (UTILS_UNLIKELY(!success)) {
|
if (!success) {
|
||||||
// reset current error to EGL_SUCCESS
|
|
||||||
eglGetError();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
outFrameTimestamps->requestedPresentTime = values[0];
|
outFrameTimestamps->requestedPresentTime = values[0];
|
||||||
@@ -390,44 +361,28 @@ bool PlatformEGLAndroid::queryFrameTimestamps(SwapChain const* swapchain, uint64
|
|||||||
outFrameTimestamps->releaseTime = values[8];
|
outFrameTimestamps->releaseTime = values[8];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fallback to private APIs
|
||||||
|
auto const anw = sc->nativeWindow;
|
||||||
|
int const status = NativeWindow::getFrameTimestamps(anw, hwFrameId,
|
||||||
|
&outFrameTimestamps->requestedPresentTime,
|
||||||
|
&outFrameTimestamps->acquireTime,
|
||||||
|
&outFrameTimestamps->latchTime,
|
||||||
|
&outFrameTimestamps->firstCompositionStartTime,
|
||||||
|
&outFrameTimestamps->lastCompositionStartTime,
|
||||||
|
&outFrameTimestamps->gpuCompositionDoneTime,
|
||||||
|
&outFrameTimestamps->displayPresentTime,
|
||||||
|
&outFrameTimestamps->dequeueReadyTime,
|
||||||
|
&outFrameTimestamps->releaseTime);
|
||||||
|
if (status == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return PlatformEGL::queryFrameTimestamps(swapchain, frameId, outFrameTimestamps);
|
return PlatformEGL::queryFrameTimestamps(swapchain, frameId, outFrameTimestamps);
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform::SwapChain* PlatformEGLAndroid::createSwapChain(void* nativeWindow, uint64_t const flags) {
|
Platform::SwapChain* PlatformEGLAndroid::createSwapChain(void* nativeWindow, uint64_t const flags) {
|
||||||
auto* const sc = new(std::nothrow) SwapChainEGLAndroid(*this, nativeWindow, flags);
|
auto* const sc = new(std::nothrow) SwapChainEGLAndroid(*this, nativeWindow, flags);
|
||||||
if (UTILS_LIKELY(ext.egl.ANDROID_get_frame_timestamps)) {
|
|
||||||
EGLDisplay const dpy = getEglDisplay();
|
|
||||||
sc->compositorTimingSupported =
|
|
||||||
eglGetCompositorTimingSupportedANDROID(dpy, sc->sur,
|
|
||||||
EGL_COMPOSITE_DEADLINE_ANDROID) &&
|
|
||||||
eglGetCompositorTimingSupportedANDROID(dpy, sc->sur,
|
|
||||||
EGL_COMPOSITE_INTERVAL_ANDROID) &&
|
|
||||||
eglGetCompositorTimingSupportedANDROID(dpy, sc->sur,
|
|
||||||
EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID);
|
|
||||||
sc->frameTimestampsSupported =
|
|
||||||
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
|
|
||||||
EGL_REQUESTED_PRESENT_TIME_ANDROID) &&
|
|
||||||
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
|
|
||||||
EGL_RENDERING_COMPLETE_TIME_ANDROID) &&
|
|
||||||
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
|
|
||||||
EGL_COMPOSITION_LATCH_TIME_ANDROID) &&
|
|
||||||
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
|
|
||||||
EGL_FIRST_COMPOSITION_START_TIME_ANDROID) &&
|
|
||||||
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
|
|
||||||
EGL_LAST_COMPOSITION_START_TIME_ANDROID) &&
|
|
||||||
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
|
|
||||||
EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID) &&
|
|
||||||
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
|
|
||||||
EGL_DISPLAY_PRESENT_TIME_ANDROID) &&
|
|
||||||
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
|
|
||||||
EGL_DEQUEUE_READY_TIME_ANDROID) &&
|
|
||||||
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
|
|
||||||
EGL_READS_DONE_TIME_ANDROID);
|
|
||||||
}
|
|
||||||
// This is expected to be a low frequency log, only turned on in debug builds
|
|
||||||
DLOG(INFO) << "anw: " << nativeWindow
|
|
||||||
<< ", compositorTimingSupported=" << sc->compositorTimingSupported
|
|
||||||
<< ", frameTimestampsSupported=" << sc->frameTimestampsSupported;
|
|
||||||
return sc;
|
return sc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -720,12 +675,12 @@ AcquiredImage PlatformEGLAndroid::transformAcquiredImage(AcquiredImage const sou
|
|||||||
|
|
||||||
|
|
||||||
bool PlatformEGLAndroid::isProducerThrottlingControlSupported() const {
|
bool PlatformEGLAndroid::isProducerThrottlingControlSupported() const {
|
||||||
return mAndroidDetails.producerThrottling.isSupported();
|
return mProducerThrottling.isSupported();
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t PlatformEGLAndroid::setProducerThrottlingEnabled(
|
int32_t PlatformEGLAndroid::setProducerThrottlingEnabled(
|
||||||
EGLNativeWindowType const nativeWindow, bool const enabled) const {
|
EGLNativeWindowType const nativeWindow, bool const enabled) const {
|
||||||
return mAndroidDetails.producerThrottling.setProducerThrottlingEnabled(nativeWindow, enabled);
|
return mProducerThrottling.setProducerThrottlingEnabled(nativeWindow, enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------
|
||||||
@@ -749,6 +704,8 @@ PlatformEGLAndroid::SwapChainEGLAndroid::SwapChainEGLAndroid(PlatformEGLAndroid
|
|||||||
// we ignore the result, it doesn't matter much if it fails
|
// we ignore the result, it doesn't matter much if it fails
|
||||||
eglSurfaceAttrib(platform.getEglDisplay(), sur, EGL_TIMESTAMPS_ANDROID, EGL_TRUE);
|
eglSurfaceAttrib(platform.getEglDisplay(), sur, EGL_TIMESTAMPS_ANDROID, EGL_TRUE);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
NativeWindow::enableFrameTimestamps(EGLNativeWindowType(nativeWindow), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -921,6 +921,14 @@ void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
// on Android, disable producer throttling
|
||||||
|
if (mProducerThrottling.isSupported()) {
|
||||||
|
mProducerThrottling.setProducerThrottlingEnabled(
|
||||||
|
static_cast<ANativeWindow*>(nativeWindow), false);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
auto swapChain = resource_ptr<VulkanSwapChain>::make(&mResourceManager, sch, mPlatform,
|
auto swapChain = resource_ptr<VulkanSwapChain>::make(&mResourceManager, sch, mPlatform,
|
||||||
mContext, &mResourceManager, mAllocator, &mCommands, mStagePool, nativeWindow, flags);
|
mContext, &mResourceManager, mAllocator, &mCommands, mStagePool, nativeWindow, flags);
|
||||||
swapChain.inc();
|
swapChain.inc();
|
||||||
|
|||||||
@@ -48,6 +48,10 @@
|
|||||||
#include <utils/Allocator.h>
|
#include <utils/Allocator.h>
|
||||||
#include <utils/compiler.h>
|
#include <utils/compiler.h>
|
||||||
|
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
#include "AndroidNativeWindow.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
class VulkanPlatform;
|
class VulkanPlatform;
|
||||||
@@ -202,6 +206,9 @@ private:
|
|||||||
bool const mIsSRGBSwapChainSupported;
|
bool const mIsSRGBSwapChainSupported;
|
||||||
bool const mIsMSAASwapChainSupported;
|
bool const mIsMSAASwapChainSupported;
|
||||||
backend::StereoscopicType const mStereoscopicType;
|
backend::StereoscopicType const mStereoscopicType;
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
AndroidProducerThrottling mProducerThrottling;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace filament::backend
|
} // namespace filament::backend
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <backend/platforms/VulkanPlatformAndroid.h>
|
#include <backend/platforms/VulkanPlatformAndroid.h>
|
||||||
|
|
||||||
#include "vulkan/VulkanConstants.h"
|
#include "vulkan/VulkanConstants.h"
|
||||||
@@ -21,8 +20,6 @@
|
|||||||
#include "vulkan/platform/VulkanPlatformSwapChainImpl.h"
|
#include "vulkan/platform/VulkanPlatformSwapChainImpl.h"
|
||||||
#include "vulkan/utils/Image.h"
|
#include "vulkan/utils/Image.h"
|
||||||
|
|
||||||
#include "AndroidFrameCallback.h"
|
|
||||||
|
|
||||||
#include <private/backend/BackendUtilsAndroid.h>
|
#include <private/backend/BackendUtilsAndroid.h>
|
||||||
|
|
||||||
#include <backend/DriverEnums.h>
|
#include <backend/DriverEnums.h>
|
||||||
@@ -35,7 +32,6 @@
|
|||||||
#include <android/hardware_buffer.h>
|
#include <android/hardware_buffer.h>
|
||||||
#include <android/native_window.h>
|
#include <android/native_window.h>
|
||||||
|
|
||||||
#include <new>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
using namespace bluevk;
|
using namespace bluevk;
|
||||||
@@ -204,14 +200,6 @@ uint32_t selectMemoryTypeForExternalImage(VkPhysicalDevice physicalDevice, VkDev
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
struct VulkanPlatformAndroid::AndroidDetails {
|
|
||||||
AndroidFrameCallback androidFrameCallback;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() {
|
VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() {
|
||||||
if (__builtin_available(android 26, *)) {
|
if (__builtin_available(android 26, *)) {
|
||||||
if (aHardwareBuffer) {
|
if (aHardwareBuffer) {
|
||||||
@@ -220,19 +208,14 @@ VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------
|
VulkanPlatformAndroid::VulkanPlatformAndroid() {
|
||||||
|
|
||||||
VulkanPlatformAndroid::VulkanPlatformAndroid() :
|
|
||||||
mAndroidDetails(*(new(std::nothrow) AndroidDetails{})) {
|
|
||||||
mOSVersion = android_get_device_api_level();
|
mOSVersion = android_get_device_api_level();
|
||||||
if (mOSVersion < 0) {
|
if (mOSVersion < 0) {
|
||||||
mOSVersion = __ANDROID_API_FUTURE__;
|
mOSVersion = __ANDROID_API_FUTURE__;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VulkanPlatformAndroid::~VulkanPlatformAndroid() noexcept {
|
VulkanPlatformAndroid::~VulkanPlatformAndroid() noexcept = default;
|
||||||
delete &mAndroidDetails;
|
|
||||||
}
|
|
||||||
|
|
||||||
Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage(
|
Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage(
|
||||||
AHardwareBuffer const* buffer, bool sRGB) noexcept {
|
AHardwareBuffer const* buffer, bool sRGB) noexcept {
|
||||||
@@ -514,14 +497,14 @@ int VulkanPlatformAndroid::getOSVersion() const noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VulkanPlatformAndroid::terminate() {
|
void VulkanPlatformAndroid::terminate() {
|
||||||
mAndroidDetails.androidFrameCallback.terminate();
|
mAndroidFrameCallback.terminate();
|
||||||
VulkanPlatform::terminate();
|
VulkanPlatform::terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
Driver* VulkanPlatformAndroid::createDriver(void* sharedContext, DriverConfig const& driverConfig) {
|
Driver* VulkanPlatformAndroid::createDriver(void* sharedContext, DriverConfig const& driverConfig) {
|
||||||
Driver* driver = VulkanPlatform::createDriver(sharedContext, driverConfig);
|
Driver* driver = VulkanPlatform::createDriver(sharedContext, driverConfig);
|
||||||
if (driver) {
|
if (driver) {
|
||||||
mAndroidDetails.androidFrameCallback.init();
|
mAndroidFrameCallback.init();
|
||||||
}
|
}
|
||||||
return driver;
|
return driver;
|
||||||
}
|
}
|
||||||
@@ -537,19 +520,13 @@ bool VulkanPlatformAndroid::queryCompositorTiming(SwapChain const* swapchain,
|
|||||||
}
|
}
|
||||||
|
|
||||||
AndroidFrameCallback::Timeline const preferredTimeline{
|
AndroidFrameCallback::Timeline const preferredTimeline{
|
||||||
mAndroidDetails.androidFrameCallback.getPreferredTimeline() };
|
mAndroidFrameCallback.getPreferredTimeline() };
|
||||||
outCompositorTiming->frameTime = preferredTimeline.frameTime;
|
outCompositorTiming->frameTime = preferredTimeline.frameTime;
|
||||||
outCompositorTiming->expectedPresentTime = preferredTimeline.expectedPresentTime;
|
outCompositorTiming->expectedPresentTime = preferredTimeline.expectedPresentTime;
|
||||||
outCompositorTiming->frameTimelineDeadline = preferredTimeline.frameTimelineDeadline;
|
outCompositorTiming->frameTimelineDeadline = preferredTimeline.frameTimelineDeadline;
|
||||||
outCompositorTiming->compositeDeadline = CompositorTiming::INVALID;
|
|
||||||
outCompositorTiming->compositeInterval = CompositorTiming::INVALID;
|
|
||||||
outCompositorTiming->compositeToPresentLatency = CompositorTiming::INVALID;
|
|
||||||
|
|
||||||
// From this point on, we always return "success" because some timings were returned.
|
|
||||||
|
|
||||||
auto vulkanSwapchain = static_cast<VulkanPlatformSwapChainBase const *>(swapchain);
|
auto vulkanSwapchain = static_cast<VulkanPlatformSwapChainBase const *>(swapchain);
|
||||||
vulkanSwapchain->queryCompositorTiming(outCompositorTiming);
|
return vulkanSwapchain->queryCompositorTiming(outCompositorTiming);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanPlatformAndroid::setPresentFrameId(SwapChain const* swapchain, uint64_t frameId) noexcept {
|
bool VulkanPlatformAndroid::setPresentFrameId(SwapChain const* swapchain, uint64_t frameId) noexcept {
|
||||||
|
|||||||
@@ -165,11 +165,6 @@ VulkanPlatformSurfaceSwapChain::~VulkanPlatformSurfaceSwapChain() {
|
|||||||
VkResult VulkanPlatformSurfaceSwapChain::create() {
|
VkResult VulkanPlatformSurfaceSwapChain::create() {
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
NativeWindow::enableFrameTimestamps(static_cast<ANativeWindow*>(mNativeWindow), true);
|
NativeWindow::enableFrameTimestamps(static_cast<ANativeWindow*>(mNativeWindow), true);
|
||||||
// on Android, disable producer throttling
|
|
||||||
if (mProducerThrottling.isSupported()) {
|
|
||||||
mProducerThrottling.setProducerThrottlingEnabled(
|
|
||||||
static_cast<ANativeWindow*>(mNativeWindow), false);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
VkSurfaceFormatKHR surfaceFormat = {};
|
VkSurfaceFormatKHR surfaceFormat = {};
|
||||||
@@ -361,15 +356,13 @@ bool VulkanPlatformSurfaceSwapChain::queryCompositorTiming(
|
|||||||
CompositorTiming* outCompositorTiming) const {
|
CompositorTiming* outCompositorTiming) const {
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
// fallback to private APIs
|
// fallback to private APIs
|
||||||
if (UTILS_VERY_LIKELY(mNativeWindow)) {
|
int const status = NativeWindow::getCompositorTiming(
|
||||||
int const status = NativeWindow::getCompositorTiming(
|
static_cast<ANativeWindow*>(mNativeWindow),
|
||||||
static_cast<ANativeWindow*>(mNativeWindow),
|
&outCompositorTiming->compositeDeadline,
|
||||||
&outCompositorTiming->compositeDeadline,
|
&outCompositorTiming->compositeInterval,
|
||||||
&outCompositorTiming->compositeInterval,
|
&outCompositorTiming->compositeToPresentLatency);
|
||||||
&outCompositorTiming->compositeToPresentLatency);
|
if (status == 0) {
|
||||||
if (status == 0) {
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return VulkanPlatformSwapChainBase::queryCompositorTiming(outCompositorTiming);
|
return VulkanPlatformSwapChainBase::queryCompositorTiming(outCompositorTiming);
|
||||||
|
|||||||
@@ -25,8 +25,7 @@
|
|||||||
#include <bluevk/BlueVK.h>
|
#include <bluevk/BlueVK.h>
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
#include "AndroidSwapChainHelper.h"
|
#include <AndroidSwapChainHelper.h>
|
||||||
#include "AndroidNativeWindow.h"
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@@ -123,7 +122,6 @@ private:
|
|||||||
|
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
AndroidSwapChainHelper mImpl{};
|
AndroidSwapChainHelper mImpl{};
|
||||||
AndroidProducerThrottling mProducerThrottling;
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ WebGPUBufferBase::WebGPUBufferBase(wgpu::Device const& device, const wgpu::Buffe
|
|||||||
// WebGPU requires that the size of the data copied from the staging buffer to the GPU buffer is a
|
// WebGPU requires that the size of the data copied from the staging buffer to the GPU buffer is a
|
||||||
// multiple of 4. This function handles cases where the buffer descriptor's size is not a multiple
|
// multiple of 4. This function handles cases where the buffer descriptor's size is not a multiple
|
||||||
// of 4 by padding with zeros.
|
// of 4 by padding with zeros.
|
||||||
void WebGPUBufferBase::updateGPUBuffer(BufferDescriptor const& bufferDescriptor,
|
void WebGPUBufferBase::updateGPUBuffer(BufferDescriptor&& bufferDescriptor,
|
||||||
const uint32_t byteOffset, wgpu::Device const& device,
|
const uint32_t byteOffset, wgpu::Device const& device,
|
||||||
WebGPUQueueManager* const webGPUQueueManager) {
|
WebGPUQueueManager* const webGPUQueueManager) {
|
||||||
FILAMENT_CHECK_PRECONDITION(bufferDescriptor.buffer)
|
FILAMENT_CHECK_PRECONDITION(bufferDescriptor.buffer)
|
||||||
@@ -89,24 +89,49 @@ void WebGPUBufferBase::updateGPUBuffer(BufferDescriptor const& bufferDescriptor,
|
|||||||
wgpu::BufferDescriptor descriptor{
|
wgpu::BufferDescriptor descriptor{
|
||||||
.label = "Filament WebGPU Staging Buffer",
|
.label = "Filament WebGPU Staging Buffer",
|
||||||
.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc,
|
.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc,
|
||||||
.size = stagingBufferSize,
|
.size = stagingBufferSize};
|
||||||
.mappedAtCreation = true };
|
|
||||||
wgpu::Buffer stagingBuffer = device.CreateBuffer(&descriptor);
|
wgpu::Buffer stagingBuffer = device.CreateBuffer(&descriptor);
|
||||||
|
|
||||||
void* mappedRange = stagingBuffer.GetMappedRange();
|
struct UserData final {
|
||||||
memcpy(mappedRange, bufferDescriptor.buffer, bufferDescriptor.size);
|
uint32_t byteOffset;
|
||||||
|
size_t stagingBufferSize;
|
||||||
// Make sure the padded memory is set to 0 to have deterministic behaviors
|
size_t remainder;
|
||||||
if (remainder != 0) {
|
BufferDescriptor srcBufferDescriptor;
|
||||||
uint8_t* paddingStart = static_cast<uint8_t*>(mappedRange) + bufferDescriptor.size;
|
wgpu::Buffer stagingBuffer;
|
||||||
memset(paddingStart, 0, FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS - remainder);
|
WebGPUQueueManager* const webGPUQueueManager;
|
||||||
}
|
wgpu::Buffer dstBuffer;
|
||||||
|
};
|
||||||
stagingBuffer.Unmap();
|
auto userData = std::make_unique<UserData>(UserData{
|
||||||
|
.byteOffset = byteOffset,
|
||||||
// Copy the staging buffer contents to the destination buffer.
|
.stagingBufferSize = stagingBufferSize,
|
||||||
webGPUQueueManager->getCommandEncoder().CopyBufferToBuffer(stagingBuffer, 0, mBuffer,
|
.remainder = remainder,
|
||||||
byteOffset, stagingBufferSize);
|
.srcBufferDescriptor = std::move(bufferDescriptor),
|
||||||
|
.stagingBuffer = stagingBuffer,
|
||||||
|
.webGPUQueueManager = webGPUQueueManager,
|
||||||
|
.dstBuffer = mBuffer});
|
||||||
|
stagingBuffer.MapAsync(
|
||||||
|
wgpu::MapMode::Write, 0, stagingBufferSize, wgpu::CallbackMode::AllowProcessEvents,
|
||||||
|
[](wgpu::MapAsyncStatus status, const char* message, UserData* userdata) {
|
||||||
|
std::unique_ptr<UserData> data(static_cast<UserData*>(userdata));
|
||||||
|
if (UTILS_LIKELY(status == wgpu::MapAsyncStatus::Success)) {
|
||||||
|
void* mappedRange = data->stagingBuffer.GetMappedRange();
|
||||||
|
memcpy(mappedRange, data->srcBufferDescriptor.buffer,
|
||||||
|
data->srcBufferDescriptor.size);
|
||||||
|
if (data->remainder != 0) {
|
||||||
|
uint8_t* paddingStart =
|
||||||
|
static_cast<uint8_t*>(mappedRange) + data->srcBufferDescriptor.size;
|
||||||
|
memset(paddingStart, 0,
|
||||||
|
FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS - data->remainder);
|
||||||
|
}
|
||||||
|
data->stagingBuffer.Unmap();
|
||||||
|
data->webGPUQueueManager->getCommandEncoder().CopyBufferToBuffer(
|
||||||
|
data->stagingBuffer, 0, data->dstBuffer, data->byteOffset,
|
||||||
|
data->stagingBufferSize);
|
||||||
|
} else {
|
||||||
|
FWGPU_LOGE << "Failed to map staging buffer for readPixels: " << message;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
userData.release());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace filament::backend
|
} // namespace filament::backend
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public:
|
|||||||
* happen after draw commands encoded in the encoder. Submitting any commands up to this point
|
* happen after draw commands encoded in the encoder. Submitting any commands up to this point
|
||||||
* ensures the calls happen in the expected sequence.
|
* ensures the calls happen in the expected sequence.
|
||||||
*/
|
*/
|
||||||
void updateGPUBuffer(BufferDescriptor const&, uint32_t byteOffset, wgpu::Device const& device,
|
void updateGPUBuffer(BufferDescriptor&&, uint32_t byteOffset, wgpu::Device const& device,
|
||||||
WebGPUQueueManager* const webGPUQueueManager);
|
WebGPUQueueManager* const webGPUQueueManager);
|
||||||
|
|
||||||
[[nodiscard]] wgpu::Buffer const& getBuffer() const { return mBuffer; }
|
[[nodiscard]] wgpu::Buffer const& getBuffer() const { return mBuffer; }
|
||||||
|
|||||||
@@ -856,7 +856,7 @@ void WebGPUDriver::updateIndexBuffer(Handle<HwIndexBuffer> indexBufferHandle,
|
|||||||
// draw calls are made.
|
// draw calls are made.
|
||||||
flush();
|
flush();
|
||||||
handleCast<WebGPUIndexBuffer>(indexBufferHandle)
|
handleCast<WebGPUIndexBuffer>(indexBufferHandle)
|
||||||
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager);
|
->updateGPUBuffer(std::move(bufferDescriptor), byteOffset, mDevice, &mQueueManager);
|
||||||
scheduleDestroy(std::move(bufferDescriptor));
|
scheduleDestroy(std::move(bufferDescriptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -867,14 +867,14 @@ void WebGPUDriver::updateBufferObject(Handle<HwBufferObject> bufferObjectHandle,
|
|||||||
// draw calls are made.
|
// draw calls are made.
|
||||||
flush();
|
flush();
|
||||||
handleCast<WebGPUBufferObject>(bufferObjectHandle)
|
handleCast<WebGPUBufferObject>(bufferObjectHandle)
|
||||||
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager);
|
->updateGPUBuffer(std::move(bufferDescriptor), byteOffset, mDevice, &mQueueManager);
|
||||||
scheduleDestroy(std::move(bufferDescriptor));
|
scheduleDestroy(std::move(bufferDescriptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> bufferObjectHandle,
|
void WebGPUDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> bufferObjectHandle,
|
||||||
BufferDescriptor&& bufferDescriptor, const uint32_t byteOffset) {
|
BufferDescriptor&& bufferDescriptor, const uint32_t byteOffset) {
|
||||||
handleCast<WebGPUBufferObject>(bufferObjectHandle)
|
handleCast<WebGPUBufferObject>(bufferObjectHandle)
|
||||||
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager);
|
->updateGPUBuffer(std::move(bufferDescriptor), byteOffset, mDevice, &mQueueManager);
|
||||||
scheduleDestroy(std::move(bufferDescriptor));
|
scheduleDestroy(std::move(bufferDescriptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
@@ -58,6 +59,7 @@ WebGPUQueueManager::WebGPUQueueManager(wgpu::Device const& device)
|
|||||||
WebGPUQueueManager::~WebGPUQueueManager() = default;
|
WebGPUQueueManager::~WebGPUQueueManager() = default;
|
||||||
|
|
||||||
wgpu::CommandEncoder WebGPUQueueManager::getCommandEncoder() {
|
wgpu::CommandEncoder WebGPUQueueManager::getCommandEncoder() {
|
||||||
|
// std::unique_lock<std::mutex> lock(mLock);
|
||||||
if (!mCommandEncoder) {
|
if (!mCommandEncoder) {
|
||||||
wgpu::CommandEncoderDescriptor commandEncoderDescriptor = {
|
wgpu::CommandEncoderDescriptor commandEncoderDescriptor = {
|
||||||
.label = "Filament Command Encoder",
|
.label = "Filament Command Encoder",
|
||||||
@@ -90,7 +92,9 @@ std::shared_ptr<WebGPUSubmissionState> WebGPUQueueManager::getLatestSubmissionSt
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUQueueManager::submit() {
|
void WebGPUQueueManager::submit() {
|
||||||
|
// std::unique_lock<std::mutex> lock(mLock);
|
||||||
if (!mCommandEncoder) {
|
if (!mCommandEncoder) {
|
||||||
|
std::cout << "Run Yu: no mCommandEncoder found!\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ private:
|
|||||||
wgpu::Queue mQueue;
|
wgpu::Queue mQueue;
|
||||||
wgpu::CommandEncoder mCommandEncoder;
|
wgpu::CommandEncoder mCommandEncoder;
|
||||||
std::shared_ptr<WebGPUSubmissionState> mLatestSubmissionState;
|
std::shared_ptr<WebGPUSubmissionState> mLatestSubmissionState;
|
||||||
|
std::mutex mLock;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace filament::backend
|
} // namespace filament::backend
|
||||||
|
|||||||
@@ -113,10 +113,8 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Retrieve a history of frame timing information. The maximum frame history size is
|
* Retrieve a history of frame timing information. The maximum frame history size is
|
||||||
* given by getMaxFrameHistorySize().
|
* given by getMaxFrameHistorySize().
|
||||||
* All or part of the history can be lost when using a different SwapChain in beginFrame().
|
|
||||||
* @param historySize requested history size. The returned vector could be smaller.
|
* @param historySize requested history size. The returned vector could be smaller.
|
||||||
* @return A vector of FrameInfo.
|
* @return A vector of FrameInfo.
|
||||||
* @see beginFrame()
|
|
||||||
*/
|
*/
|
||||||
utils::FixedCapacityVector<FrameInfo> getFrameInfoHistory(
|
utils::FixedCapacityVector<FrameInfo> getFrameInfoHistory(
|
||||||
size_t historySize = 1) const noexcept;
|
size_t historySize = 1) const noexcept;
|
||||||
@@ -328,8 +326,6 @@ public:
|
|||||||
* or 0 if unknown. This value should be the timestamp of
|
* or 0 if unknown. This value should be the timestamp of
|
||||||
* the last h/w vsync. It is expressed in the
|
* the last h/w vsync. It is expressed in the
|
||||||
* std::chrono::steady_clock time base.
|
* std::chrono::steady_clock time base.
|
||||||
* On Android this should be the frame time received from
|
|
||||||
* a Choreographer.
|
|
||||||
* @param swapChain A pointer to the SwapChain instance to use.
|
* @param swapChain A pointer to the SwapChain instance to use.
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
@@ -341,8 +337,6 @@ public:
|
|||||||
*
|
*
|
||||||
* @note
|
* @note
|
||||||
* All calls to render() must happen *after* beginFrame().
|
* All calls to render() must happen *after* beginFrame().
|
||||||
* It is recommended to use the same swapChain for every call to beginFrame, failing to do
|
|
||||||
* so can result is losing all or part of the FrameInfo history.
|
|
||||||
*
|
*
|
||||||
* @see
|
* @see
|
||||||
* endFrame()
|
* endFrame()
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
#include "details/BufferAllocator.h"
|
#include "details/BufferAllocator.h"
|
||||||
|
|
||||||
#include <private/utils/Tracing.h>
|
|
||||||
#include <utils/Panic.h>
|
#include <utils/Panic.h>
|
||||||
#include <utils/debug.h>
|
#include <utils/debug.h>
|
||||||
|
|
||||||
@@ -170,7 +169,6 @@ void BufferAllocator::releaseGpu(AllocationId id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BufferAllocator::releaseFreeSlots() {
|
void BufferAllocator::releaseFreeSlots() {
|
||||||
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
|
||||||
if (!mHasPendingFrees) {
|
if (!mHasPendingFrees) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -718,7 +718,8 @@ void FEngine::prepare() {
|
|||||||
|
|
||||||
if (useUboBatching) {
|
if (useUboBatching) {
|
||||||
assert_invariant(mUboManager != nullptr);
|
assert_invariant(mUboManager != nullptr);
|
||||||
mUboManager->beginFrame(driver);
|
|
||||||
|
mUboManager->beginFrame(driver, mMaterialInstances);
|
||||||
}
|
}
|
||||||
|
|
||||||
UboManager* uboManager = mUboManager;
|
UboManager* uboManager = mUboManager;
|
||||||
@@ -757,7 +758,7 @@ void FEngine::gc() {
|
|||||||
void FEngine::submitFrame() {
|
void FEngine::submitFrame() {
|
||||||
if (isUboBatchingEnabled()) {
|
if (isUboBatchingEnabled()) {
|
||||||
DriverApi& driver = getDriverApi();
|
DriverApi& driver = getDriverApi();
|
||||||
getUboManager()->endFrame(driver);
|
getUboManager()->endFrame(driver, getMaterialInstanceResourceList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1284,6 +1285,11 @@ UTILS_NOINLINE
|
|||||||
bool FEngine::destroy(const FMaterialInstance* p) {
|
bool FEngine::destroy(const FMaterialInstance* p) {
|
||||||
if (p == nullptr) return true;
|
if (p == nullptr) return true;
|
||||||
|
|
||||||
|
if (p->isUsingUboBatching()) {
|
||||||
|
assert_invariant(isUboBatchingEnabled());
|
||||||
|
mUboManager->retireSlot(p->getAllocationId());
|
||||||
|
}
|
||||||
|
|
||||||
// Check that the material instance we're destroying is not in use in the RenderableManager
|
// Check that the material instance we're destroying is not in use in the RenderableManager
|
||||||
// To do this, we currently need to inspect all render primitives in the RenderableManager
|
// To do this, we currently need to inspect all render primitives in the RenderableManager
|
||||||
EntityManager const& em = mEntityManager;
|
EntityManager const& em = mEntityManager;
|
||||||
|
|||||||
@@ -95,7 +95,6 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
|
|||||||
|
|
||||||
if (mUseUboBatching) {
|
if (mUseUboBatching) {
|
||||||
mUboData = BufferAllocator::UNALLOCATED;
|
mUboData = BufferAllocator::UNALLOCATED;
|
||||||
engine.getUboManager()->manageMaterialInstance(this);
|
|
||||||
} else {
|
} else {
|
||||||
mUboData = driver.createBufferObject(mUniforms.getSize(), BufferObjectBinding::UNIFORM,
|
mUboData = driver.createBufferObject(mUniforms.getSize(), BufferObjectBinding::UNIFORM,
|
||||||
BufferUsage::STATIC, ImmutableCString{ material->getName().c_str_safe() });
|
BufferUsage::STATIC, ImmutableCString{ material->getName().c_str_safe() });
|
||||||
@@ -168,7 +167,6 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
|
|||||||
|
|
||||||
if (mUseUboBatching) {
|
if (mUseUboBatching) {
|
||||||
mUboData = BufferAllocator::UNALLOCATED;
|
mUboData = BufferAllocator::UNALLOCATED;
|
||||||
engine.getUboManager()->manageMaterialInstance(this);
|
|
||||||
} else {
|
} else {
|
||||||
mUboData = driver.createBufferObject(mUniforms.getSize(), BufferObjectBinding::UNIFORM,
|
mUboData = driver.createBufferObject(mUniforms.getSize(), BufferObjectBinding::UNIFORM,
|
||||||
BufferUsage::DYNAMIC, ImmutableCString{ material->getName().c_str_safe() });
|
BufferUsage::DYNAMIC, ImmutableCString{ material->getName().c_str_safe() });
|
||||||
@@ -213,10 +211,6 @@ FMaterialInstance::~FMaterialInstance() noexcept = default;
|
|||||||
void FMaterialInstance::terminate(FEngine& engine) {
|
void FMaterialInstance::terminate(FEngine& engine) {
|
||||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||||
mDescriptorSet.terminate(driver);
|
mDescriptorSet.terminate(driver);
|
||||||
if (mUseUboBatching) {
|
|
||||||
engine.getUboManager()->unmanageMaterialInstance(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* ubHandle = std::get_if<Handle<HwBufferObject>>(&mUboData);
|
auto* ubHandle = std::get_if<Handle<HwBufferObject>>(&mUboData);
|
||||||
if (ubHandle){
|
if (ubHandle){
|
||||||
driver.destroyBufferObject(*ubHandle);
|
driver.destroyBufferObject(*ubHandle);
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ void UboManager::FenceManager::track(DriverApi& driver, std::unordered_set<Alloc
|
|||||||
|
|
||||||
void UboManager::FenceManager::reclaimCompletedResources(DriverApi& driver,
|
void UboManager::FenceManager::reclaimCompletedResources(DriverApi& driver,
|
||||||
std::function<void(AllocationId)> const& onReclaimed) {
|
std::function<void(AllocationId)> const& onReclaimed) {
|
||||||
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
|
||||||
uint32_t signaledCount = 0;
|
uint32_t signaledCount = 0;
|
||||||
bool seenSignaledFence = false;
|
bool seenSignaledFence = false;
|
||||||
|
|
||||||
@@ -116,7 +115,8 @@ UboManager::UboManager(DriverApi& driver, allocation_size_t defaultSlotSizeInByt
|
|||||||
reallocate(driver, defaultTotalSizeInBytes);
|
reallocate(driver, defaultTotalSizeInBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UboManager::beginFrame(DriverApi& driver) {
|
void UboManager::beginFrame(DriverApi& driver,
|
||||||
|
const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>& materialInstances) {
|
||||||
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||||
// Check finished frames and decrement GPU count accordingly.
|
// Check finished frames and decrement GPU count accordingly.
|
||||||
mFenceManager.reclaimCompletedResources(driver,
|
mFenceManager.reclaimCompletedResources(driver,
|
||||||
@@ -126,7 +126,7 @@ void UboManager::beginFrame(DriverApi& driver) {
|
|||||||
mAllocator.releaseFreeSlots();
|
mAllocator.releaseFreeSlots();
|
||||||
|
|
||||||
// Traverse all MIs and see which of them need slot allocation.
|
// Traverse all MIs and see which of them need slot allocation.
|
||||||
if (allocateOnDemand() == SUCCESS) {
|
if (allocateOnDemand(materialInstances) == SUCCESS) {
|
||||||
// No need to grow the buffer, so we can just map the buffer for writing and return.
|
// No need to grow the buffer, so we can just map the buffer for writing and return.
|
||||||
mMemoryMappedBufferHandle = driver.mapBuffer(mUbHandle, 0, mUboSize, MapBufferAccessFlags::WRITE_BIT,
|
mMemoryMappedBufferHandle = driver.mapBuffer(mUbHandle, 0, mUboSize, MapBufferAccessFlags::WRITE_BIT,
|
||||||
"UboManager");
|
"UboManager");
|
||||||
@@ -135,19 +135,25 @@ void UboManager::beginFrame(DriverApi& driver) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the required size and grow the Ubo.
|
// Calculate the required size and grow the Ubo.
|
||||||
const allocation_size_t requiredSize = calculateRequiredSize();
|
const allocation_size_t requiredSize = calculateRequiredSize(materialInstances);
|
||||||
reallocate(driver, requiredSize);
|
reallocate(driver, requiredSize);
|
||||||
|
|
||||||
// Allocate slots for each MI on the new Ubo.
|
// Allocate slots for each MI on the new Ubo.
|
||||||
allocateAllInstances();
|
allocateAllInstances(materialInstances);
|
||||||
|
|
||||||
// Map the buffer so that we can write to it
|
// Map the buffer so that we can write to it
|
||||||
mMemoryMappedBufferHandle =
|
mMemoryMappedBufferHandle =
|
||||||
driver.mapBuffer(mUbHandle, 0, mUboSize, MapBufferAccessFlags::WRITE_BIT, "UboManager");
|
driver.mapBuffer(mUbHandle, 0, mUboSize, MapBufferAccessFlags::WRITE_BIT, "UboManager");
|
||||||
|
|
||||||
// Invalidate the migrated MIs, so that next commit() call must be triggered.
|
// Invalidate the migrated MIs, so that next commit() call must be triggered.
|
||||||
for (const auto* mi : mManagedInstances) {
|
for (const auto& materialInstance : materialInstances) {
|
||||||
mi->getUniformBuffer().invalidate();
|
materialInstance.second.forEach([](const FMaterialInstance* mi) {
|
||||||
|
if (!mi->isUsingUboBatching()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mi->getUniformBuffer().invalidate();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,16 +164,24 @@ void UboManager::finishBeginFrame(DriverApi& driver) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UboManager::endFrame(DriverApi& driver) {
|
void UboManager::endFrame(DriverApi& driver,
|
||||||
|
const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>& materialInstances) {
|
||||||
|
BufferAllocator& allocator = mAllocator;
|
||||||
std::unordered_set<AllocationId> allocationIds;
|
std::unordered_set<AllocationId> allocationIds;
|
||||||
for (const auto* mi : mManagedInstances) {
|
for (const auto& materialInstance : materialInstances) {
|
||||||
const AllocationId id = mi->getAllocationId();
|
materialInstance.second.forEach([&allocator, &allocationIds](const FMaterialInstance* mi) {
|
||||||
if (UTILS_UNLIKELY(!BufferAllocator::isValid(id))) {
|
if (!mi->isUsingUboBatching()) {
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mAllocator.acquireGpu(id);
|
const AllocationId id = mi->getAllocationId();
|
||||||
allocationIds.insert(id);
|
if (!BufferAllocator::isValid(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
allocator.acquireGpu(id);
|
||||||
|
allocationIds.insert(id);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
mFenceManager.track(driver, std::move(allocationIds));
|
mFenceManager.track(driver, std::move(allocationIds));
|
||||||
@@ -180,90 +194,76 @@ void UboManager::terminate(DriverApi& driver) {
|
|||||||
|
|
||||||
void UboManager::updateSlot(DriverApi& driver, AllocationId id,
|
void UboManager::updateSlot(DriverApi& driver, AllocationId id,
|
||||||
BufferDescriptor bufferDescriptor) const {
|
BufferDescriptor bufferDescriptor) const {
|
||||||
if (!mMemoryMappedBufferHandle) {
|
if (!mMemoryMappedBufferHandle)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
const allocation_size_t offset = mAllocator.getAllocationOffset(id);
|
const allocation_size_t offset = mAllocator.getAllocationOffset(id);
|
||||||
driver.copyToMemoryMappedBuffer(mMemoryMappedBufferHandle, offset, std::move(bufferDescriptor));
|
driver.copyToMemoryMappedBuffer(mMemoryMappedBufferHandle, offset, std::move(bufferDescriptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
void UboManager::manageMaterialInstance(FMaterialInstance* instance) {
|
void UboManager::retireSlot(BufferAllocator::AllocationId id) {
|
||||||
mPendingInstances.insert(instance);
|
if (!BufferAllocator::isValid(id))
|
||||||
|
return;
|
||||||
|
mAllocator.retire(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UboManager::unmanageMaterialInstance(FMaterialInstance* materialInstance) {
|
UboManager::AllocationResult UboManager::allocateOnDemand(
|
||||||
AllocationId id = materialInstance->getAllocationId();
|
const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>&
|
||||||
mPendingInstances.erase(materialInstance);
|
materialInstances) {
|
||||||
mManagedInstances.erase(materialInstance);
|
// Collect all MIs that need allocation into two groups.
|
||||||
|
std::vector<FMaterialInstance*> newInstances;
|
||||||
if (!BufferAllocator::isValid(id)) {
|
std::vector<FMaterialInstance*> existingInstances;
|
||||||
return;
|
for (const auto& [_, miList] : materialInstances) {
|
||||||
|
miList.forEach([&](FMaterialInstance* mi) {
|
||||||
|
if (!mi->isUsingUboBatching()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (BufferAllocator::isValid(mi->getAllocationId())) {
|
||||||
|
existingInstances.push_back(mi);
|
||||||
|
} else {
|
||||||
|
newInstances.push_back(mi);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
mAllocator.retire(id);
|
|
||||||
materialInstance->assignUboAllocation(mUbHandle, BufferAllocator::UNALLOCATED, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
UboManager::AllocationResult UboManager::allocateOnDemand() {
|
|
||||||
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
|
||||||
bool reallocationNeeded = false;
|
bool reallocationNeeded = false;
|
||||||
|
|
||||||
// Pass 1: Allocate slots for new material instances (that don't have a slot yet).
|
// Pass 1: Allocate slots for new material instances (that don't have a slot yet).
|
||||||
for (auto* mi : mPendingInstances) {
|
for (FMaterialInstance* mi : newInstances) {
|
||||||
mManagedInstances.insert(mi);
|
|
||||||
auto [newId, newOffset] = mAllocator.allocate(mi->getUniformBuffer().getSize());
|
auto [newId, newOffset] = mAllocator.allocate(mi->getUniformBuffer().getSize());
|
||||||
|
|
||||||
// Even if the newId is not valid, we assign it to the MI so that the following process knows
|
|
||||||
// this material instance was not allocated successfully. Then we can calculate the new
|
|
||||||
// required UBO size properly.
|
|
||||||
mi->assignUboAllocation(mUbHandle, newId, newOffset);
|
mi->assignUboAllocation(mUbHandle, newId, newOffset);
|
||||||
|
|
||||||
if (!BufferAllocator::isValid(newId)) {
|
if (!BufferAllocator::isValid(newId)) {
|
||||||
reallocationNeeded = true;
|
reallocationNeeded = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mPendingInstances.clear();
|
|
||||||
|
|
||||||
// Pass 2: Allocate slots for existing material instances that need to be orphaned.
|
// Pass 2: Allocate slots for existing material instances that need to be orphaned.
|
||||||
for (auto* mi: mManagedInstances) {
|
for (FMaterialInstance* mi : existingInstances) {
|
||||||
if (!BufferAllocator::isValid(mi->getAllocationId())) {
|
if (mi->getUniformBuffer().isDirty() && mAllocator.isLockedByGpu(mi->getAllocationId())) {
|
||||||
continue;
|
mAllocator.retire(mi->getAllocationId());
|
||||||
}
|
auto [newId, newOffset] = mAllocator.allocate(mi->getUniformBuffer().getSize());
|
||||||
|
mi->assignUboAllocation(mUbHandle, newId, newOffset);
|
||||||
// This instance doesn't need orphaning.
|
if (!BufferAllocator::isValid(newId)) {
|
||||||
if (!mi->getUniformBuffer().isDirty() || !mAllocator.isLockedByGpu(mi->getAllocationId())) {
|
reallocationNeeded = true;
|
||||||
continue;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
mAllocator.retire(mi->getAllocationId());
|
|
||||||
|
|
||||||
// If the space is already not sufficient, we don't need to give another try on allocation.
|
|
||||||
if (reallocationNeeded) {
|
|
||||||
mi->assignUboAllocation(mUbHandle, REALLOCATION_REQUIRED, 0);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto [newId, newOffset] = mAllocator.allocate(mi->getUniformBuffer().getSize());
|
|
||||||
|
|
||||||
// Even if the newId is not valid, we assign it to the MI so that the following process knows
|
|
||||||
// this material instance was not allocated successfully. Then we can calculate the new
|
|
||||||
// required UBO size properly.
|
|
||||||
mi->assignUboAllocation(mUbHandle, newId, newOffset);
|
|
||||||
|
|
||||||
if (!BufferAllocator::isValid(newId)) {
|
|
||||||
reallocationNeeded = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return reallocationNeeded ? REALLOCATION_REQUIRED : SUCCESS;
|
return reallocationNeeded ? REALLOCATION_REQUIRED : SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UboManager::allocateAllInstances() {
|
void UboManager::allocateAllInstances(
|
||||||
for (auto* mi: mManagedInstances) {
|
const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>&
|
||||||
auto [newId, newOffset] = mAllocator.allocate(mi->getUniformBuffer().getSize());
|
materialInstances) {
|
||||||
assert_invariant(BufferAllocator::isValid(newId));
|
for (const auto& [_, miList] : materialInstances) {
|
||||||
mi->assignUboAllocation(mUbHandle, newId, newOffset);
|
miList.forEach([this](FMaterialInstance* mi) {
|
||||||
|
if (!mi->isUsingUboBatching()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto [newId, newOffset] = mAllocator.allocate(mi->getUniformBuffer().getSize());
|
||||||
|
assert_invariant(BufferAllocator::isValid(newId));
|
||||||
|
mi->assignUboAllocation(mUbHandle, newId, newOffset);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,19 +288,28 @@ void UboManager::reallocate(DriverApi& driver, allocation_size_t requiredSize) {
|
|||||||
BufferUsage::DYNAMIC | BufferUsage::SHARED_WRITE_BIT);
|
BufferUsage::DYNAMIC | BufferUsage::SHARED_WRITE_BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
allocation_size_t UboManager::calculateRequiredSize() {
|
allocation_size_t UboManager::calculateRequiredSize(
|
||||||
|
const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>&
|
||||||
|
materialInstances) {
|
||||||
|
BufferAllocator& allocator = mAllocator;
|
||||||
allocation_size_t newBufferSize = 0;
|
allocation_size_t newBufferSize = 0;
|
||||||
for (const auto* mi: mManagedInstances) {
|
for (const auto& materialInstance: materialInstances) {
|
||||||
const AllocationId allocationId = mi->getAllocationId();
|
materialInstance.second.forEach([&newBufferSize, &allocator](const FMaterialInstance* mi) {
|
||||||
if (allocationId == BufferAllocator::REALLOCATION_REQUIRED) {
|
if (!mi->isUsingUboBatching()) {
|
||||||
// For MIs whose parameters have been updated, aside from the slot it is being
|
return;
|
||||||
// occupied by the GPU, we need to preserve an additional slot for it.
|
}
|
||||||
newBufferSize += 2 * mAllocator.alignUp(mi->getUniformBuffer().getSize());
|
|
||||||
} else {
|
const AllocationId allocationId = mi->getAllocationId();
|
||||||
newBufferSize += mAllocator.alignUp(mi->getUniformBuffer().getSize());
|
if (allocationId == BufferAllocator::REALLOCATION_REQUIRED) {
|
||||||
}
|
// For MIs whose parameters have been updated, aside from the slot it is being
|
||||||
|
// occupied by the GPU, we need to preserve an additional slot for it.
|
||||||
|
newBufferSize += 2 * allocator.alignUp(mi->getUniformBuffer().getSize());
|
||||||
|
} else {
|
||||||
|
newBufferSize += allocator.alignUp(mi->getUniformBuffer().getSize());
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return mAllocator.alignUp(newBufferSize * BUFFER_SIZE_GROWTH_MULTIPLIER);
|
return allocator.alignUp(newBufferSize * BUFFER_SIZE_GROWTH_MULTIPLIER);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace filament
|
} // namespace filament
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#ifndef TNT_FILAMENT_DETAILS_UBOMANAGER_H
|
#ifndef TNT_FILAMENT_DETAILS_UBOMANAGER_H
|
||||||
#define TNT_FILAMENT_DETAILS_UBOMANAGER_H
|
#define TNT_FILAMENT_DETAILS_UBOMANAGER_H
|
||||||
|
|
||||||
|
#include "ResourceList.h"
|
||||||
#include "backend/DriverApiForward.h"
|
#include "backend/DriverApiForward.h"
|
||||||
|
|
||||||
#include "details/BufferAllocator.h"
|
#include "details/BufferAllocator.h"
|
||||||
@@ -28,8 +29,6 @@
|
|||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class UboManagerTest;
|
|
||||||
|
|
||||||
namespace filament {
|
namespace filament {
|
||||||
|
|
||||||
class FMaterial;
|
class FMaterial;
|
||||||
@@ -97,8 +96,9 @@ public:
|
|||||||
// instances with modified uniforms).
|
// instances with modified uniforms).
|
||||||
// 3. Reallocating a larger shared UBO if the current one is insufficient.
|
// 3. Reallocating a larger shared UBO if the current one is insufficient.
|
||||||
// 4. Mapping the shared UBO into CPU-accessible memory to prepare for uniform data writes.
|
// 4. Mapping the shared UBO into CPU-accessible memory to prepare for uniform data writes.
|
||||||
// Note that it must happen before committing all MIs.
|
void beginFrame(backend::DriverApi& driver,
|
||||||
void beginFrame(backend::DriverApi& driver);
|
const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>&
|
||||||
|
materialInstances);
|
||||||
|
|
||||||
// Unmap the buffer here
|
// Unmap the buffer here
|
||||||
void finishBeginFrame(backend::DriverApi& driver);
|
void finishBeginFrame(backend::DriverApi& driver);
|
||||||
@@ -106,31 +106,23 @@ public:
|
|||||||
// Create a fence and associate it with a set of allocation ids.
|
// Create a fence and associate it with a set of allocation ids.
|
||||||
// The gpuUseCount of these allocations will be incremented, and they will be decremented
|
// The gpuUseCount of these allocations will be incremented, and they will be decremented
|
||||||
// After the corresponding frame has been done.
|
// After the corresponding frame has been done.
|
||||||
void endFrame(backend::DriverApi& driver);
|
void endFrame(backend::DriverApi& driver,
|
||||||
|
const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>&
|
||||||
|
materialInstances);
|
||||||
|
|
||||||
void terminate(backend::DriverApi& driver);
|
void terminate(backend::DriverApi& driver);
|
||||||
|
|
||||||
void updateSlot(backend::DriverApi& driver, BufferAllocator::AllocationId id,
|
void updateSlot(backend::DriverApi& driver, BufferAllocator::AllocationId id,
|
||||||
backend::BufferDescriptor bufferDescriptor) const;
|
backend::BufferDescriptor bufferDescriptor) const;
|
||||||
|
|
||||||
// Call this to register a new material instance to UboManager.
|
// Call this when a material instance is no longer holding a slot. e.g. it is destroyed.
|
||||||
void manageMaterialInstance(FMaterialInstance* instance);
|
void retireSlot(BufferAllocator::AllocationId id);
|
||||||
|
|
||||||
// Call this when a material instance is destroyed.
|
|
||||||
void unmanageMaterialInstance(FMaterialInstance* materialInstance);
|
|
||||||
|
|
||||||
// Returns the size of the actual UBO. Note that when there's allocation failed, it will be
|
// Returns the size of the actual UBO. Note that when there's allocation failed, it will be
|
||||||
// reallocated to a bigger size at the next frame.
|
// reallocated to a bigger size at the next frame.
|
||||||
[[nodiscard]] BufferAllocator::allocation_size_t getTotalSize() const noexcept;
|
[[nodiscard]] BufferAllocator::allocation_size_t getTotalSize() const noexcept;
|
||||||
|
|
||||||
// For testing
|
|
||||||
[[nodiscard]] backend::MemoryMappedBufferHandle getMemoryMappedBufferHandle() const noexcept {
|
|
||||||
return mMemoryMappedBufferHandle;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class ::UboManagerTest;
|
|
||||||
|
|
||||||
constexpr static float BUFFER_SIZE_GROWTH_MULTIPLIER = 1.5f;
|
constexpr static float BUFFER_SIZE_GROWTH_MULTIPLIER = 1.5f;
|
||||||
|
|
||||||
enum AllocationResult {
|
enum AllocationResult {
|
||||||
@@ -142,19 +134,23 @@ private:
|
|||||||
[[nodiscard]] BufferAllocator::allocation_size_t getAllocationOffset(
|
[[nodiscard]] BufferAllocator::allocation_size_t getAllocationOffset(
|
||||||
BufferAllocator::AllocationId id) const;
|
BufferAllocator::AllocationId id) const;
|
||||||
|
|
||||||
AllocationResult allocateOnDemand();
|
AllocationResult allocateOnDemand(
|
||||||
|
const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>&
|
||||||
|
materialInstances);
|
||||||
|
|
||||||
void allocateAllInstances();
|
void allocateAllInstances(
|
||||||
|
const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>&
|
||||||
|
materialInstances);
|
||||||
|
|
||||||
void reallocate(backend::DriverApi& driver, BufferAllocator::allocation_size_t requiredSize);
|
void reallocate(backend::DriverApi& driver, BufferAllocator::allocation_size_t requiredSize);
|
||||||
|
|
||||||
BufferAllocator::allocation_size_t calculateRequiredSize();
|
BufferAllocator::allocation_size_t calculateRequiredSize(
|
||||||
|
const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>&
|
||||||
|
materialInstances);
|
||||||
|
|
||||||
backend::Handle<backend::HwBufferObject> mUbHandle;
|
backend::Handle<backend::HwBufferObject> mUbHandle;
|
||||||
backend::MemoryMappedBufferHandle mMemoryMappedBufferHandle;
|
backend::MemoryMappedBufferHandle mMemoryMappedBufferHandle;
|
||||||
BufferAllocator::allocation_size_t mUboSize{};
|
BufferAllocator::allocation_size_t mUboSize{};
|
||||||
std::unordered_set<FMaterialInstance*> mPendingInstances;
|
|
||||||
std::unordered_set<FMaterialInstance*> mManagedInstances;
|
|
||||||
|
|
||||||
FenceManager mFenceManager;
|
FenceManager mFenceManager;
|
||||||
BufferAllocator mAllocator;
|
BufferAllocator mAllocator;
|
||||||
|
|||||||
@@ -285,45 +285,53 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const Builder& builder)
|
|||||||
|
|
||||||
// calculate buffer sizes
|
// calculate buffer sizes
|
||||||
size_t bufferSizes[MAX_VERTEX_BUFFER_COUNT] = {};
|
size_t bufferSizes[MAX_VERTEX_BUFFER_COUNT] = {};
|
||||||
|
#pragma nounroll
|
||||||
auto shouldCreateBuffer = [this](size_t attributeIndex) {
|
for (size_t i = 0, n = mAttributes.size(); i < n; ++i) {
|
||||||
const uint8_t slot = mAttributes[attributeIndex].buffer;
|
if (mDeclaredAttributes[i]) {
|
||||||
return mDeclaredAttributes[attributeIndex] && slot != Attribute::BUFFER_UNUSED &&
|
const uint32_t offset = mAttributes[i].offset;
|
||||||
!mBufferObjects[slot];
|
const uint8_t stride = mAttributes[i].stride;
|
||||||
};
|
const uint8_t slot = mAttributes[i].buffer;
|
||||||
auto updateBufferSize = [&bufferSizes, this](size_t attributeIndex) {
|
const size_t end = offset + mVertexCount * stride;
|
||||||
const uint32_t offset = mAttributes[attributeIndex].offset;
|
if (slot != Attribute::BUFFER_UNUSED) {
|
||||||
const uint8_t stride = mAttributes[attributeIndex].stride;
|
assert_invariant(slot < MAX_VERTEX_BUFFER_COUNT);
|
||||||
const uint8_t slot = mAttributes[attributeIndex].buffer;
|
bufferSizes[slot] = std::max(bufferSizes[slot], end);
|
||||||
const size_t end = offset + mVertexCount * stride;
|
|
||||||
assert_invariant(slot < MAX_VERTEX_BUFFER_COUNT);
|
|
||||||
bufferSizes[slot] = std::max(bufferSizes[slot], end);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!mBufferObjectsEnabled) {
|
|
||||||
#pragma nounroll
|
|
||||||
for (size_t i = 0, n = mAttributes.size(); i < n; ++i) {
|
|
||||||
if (shouldCreateBuffer(i)) {
|
|
||||||
updateBufferSize(i);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (mAdvancedSkinningEnabled) {
|
|
||||||
// For advanced skinning mode, only relevant buffers (BONE_INDICES & BONE_WEIGHTS) are
|
|
||||||
// created. We manually populated the relevant attributes for those buffers above.
|
|
||||||
updateBufferSize(BONE_INDICES);
|
|
||||||
updateBufferSize(BONE_WEIGHTS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// create buffers
|
if (!mBufferObjectsEnabled) {
|
||||||
for (size_t i = 0; i < MAX_VERTEX_BUFFER_COUNT; ++i) {
|
// If buffer objects are not enabled at the API level, then we create them internally.
|
||||||
if (bufferSizes[i] == 0 || mBufferObjects[i]) {
|
#pragma nounroll
|
||||||
continue;
|
for (size_t index = 0; index < MAX_VERTEX_ATTRIBUTE_COUNT; ++index) {
|
||||||
|
size_t const i = mAttributes[index].buffer;
|
||||||
|
if (i != Attribute::BUFFER_UNUSED) {
|
||||||
|
assert_invariant(bufferSizes[i] > 0);
|
||||||
|
if (!mBufferObjects[i]) {
|
||||||
|
BufferObjectHandle const bo = driver.createBufferObject(bufferSizes[i],
|
||||||
|
BufferObjectBinding::VERTEX, BufferUsage::STATIC,
|
||||||
|
utils::ImmutableCString{ builder.getName() });
|
||||||
|
driver.setVertexBufferObject(mHandle, i, bo);
|
||||||
|
mBufferObjects[i] = bo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// in advanced skinning mode, we manage the BONE_INDICES and BONE_WEIGHTS arrays ourselves,
|
||||||
|
// so we have to set the corresponding buffer objects.
|
||||||
|
if (mAdvancedSkinningEnabled) {
|
||||||
|
for (auto const index : { BONE_INDICES, BONE_WEIGHTS }) {
|
||||||
|
size_t const i = mAttributes[index].buffer;
|
||||||
|
assert_invariant(i != Attribute::BUFFER_UNUSED);
|
||||||
|
assert_invariant(bufferSizes[i] > 0);
|
||||||
|
if (!mBufferObjects[i]) {
|
||||||
|
BufferObjectHandle const bo = driver.createBufferObject(bufferSizes[i],
|
||||||
|
BufferObjectBinding::VERTEX, BufferUsage::STATIC,
|
||||||
|
utils::ImmutableCString{ builder.getName() });
|
||||||
|
driver.setVertexBufferObject(mHandle, i, bo);
|
||||||
|
mBufferObjects[i] = bo;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
BufferObjectHandle const bo = driver.createBufferObject(bufferSizes[i],
|
|
||||||
BufferObjectBinding::VERTEX, BufferUsage::STATIC,
|
|
||||||
utils::ImmutableCString{ builder.getName() });
|
|
||||||
driver.setVertexBufferObject(mHandle, i, bo);
|
|
||||||
mBufferObjects[i] = bo;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,17 +50,14 @@ if (TNT_DEV)
|
|||||||
test_BufferAllocatorStress.cpp
|
test_BufferAllocatorStress.cpp
|
||||||
test_CircularQueue.cpp
|
test_CircularQueue.cpp
|
||||||
test_FenceManager.cpp
|
test_FenceManager.cpp
|
||||||
test_UboManager.cpp
|
|
||||||
filament_test_exposure.cpp
|
filament_test_exposure.cpp
|
||||||
filament_rendering_test.cpp
|
filament_rendering_test.cpp
|
||||||
filament_bimap_test.cpp
|
filament_bimap_test.cpp
|
||||||
filament_framegraph_test.cpp
|
filament_framegraph_test.cpp
|
||||||
filament_test.cpp
|
filament_test.cpp)
|
||||||
${RESGEN_SOURCE})
|
|
||||||
|
|
||||||
target_link_libraries(test_${TARGET} PRIVATE filament gtest)
|
target_link_libraries(test_${TARGET} PRIVATE filament gtest)
|
||||||
target_compile_options(test_${TARGET} PRIVATE ${COMPILER_FLAGS})
|
target_compile_options(test_${TARGET} PRIVATE ${COMPILER_FLAGS})
|
||||||
target_include_directories(test_${TARGET} PRIVATE ${RESOURCE_DIR})
|
|
||||||
set_target_properties(test_${TARGET} PROPERTIES FOLDER Tests)
|
set_target_properties(test_${TARGET} PROPERTIES FOLDER Tests)
|
||||||
|
|
||||||
add_executable(test_depth depth_test.cpp)
|
add_executable(test_depth depth_test.cpp)
|
||||||
|
|||||||
@@ -1,366 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2025 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* 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 <gmock/gmock.h>
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include "MockDriver.h"
|
|
||||||
#include "details/MaterialInstance.h"
|
|
||||||
#include "details/UboManager.h"
|
|
||||||
|
|
||||||
#include <private/backend/CommandBufferQueue.h>
|
|
||||||
#include <private/backend/CommandStream.h>
|
|
||||||
#include <private/backend/Driver.h>
|
|
||||||
|
|
||||||
#include "filament_test_resources.h"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
using namespace filament;
|
|
||||||
using namespace backend;
|
|
||||||
|
|
||||||
using ::testing::NiceMock;
|
|
||||||
|
|
||||||
using AllocationId = BufferAllocator::AllocationId;
|
|
||||||
using allocation_size_t = BufferAllocator::allocation_size_t;
|
|
||||||
} // anonymous namespace
|
|
||||||
|
|
||||||
class UboManagerTest : public ::testing::Test {
|
|
||||||
protected:
|
|
||||||
static constexpr size_t MIN_COMMAND_BUFFERS_SIZE = 1 * 1024 * 1024;
|
|
||||||
static constexpr size_t COMMAND_BUFFERS_SIZE = 3 * MIN_COMMAND_BUFFERS_SIZE;
|
|
||||||
static constexpr BufferAllocator::allocation_size_t DEFAULT_SLOT_SIZE = 64;
|
|
||||||
static constexpr BufferAllocator::allocation_size_t DEFAULT_TOTAL_SIZE = 1024;
|
|
||||||
|
|
||||||
UboManagerTest()
|
|
||||||
: mCommandBufferQueue(MIN_COMMAND_BUFFERS_SIZE, COMMAND_BUFFERS_SIZE, false),
|
|
||||||
mCommandStream(mMockDriver, mCommandBufferQueue.getCircularBuffer()),
|
|
||||||
|
|
||||||
mDriverApi(mCommandStream),
|
|
||||||
// The constructor will call reallocate, which calls createBufferObject.
|
|
||||||
// MockDriver's default ...S() implementation returns an incrementing handle.
|
|
||||||
// So, the first handle will be 1.
|
|
||||||
mUboManager(mDriverApi, DEFAULT_SLOT_SIZE, DEFAULT_TOTAL_SIZE),
|
|
||||||
mPendingInstances(mUboManager.mPendingInstances),
|
|
||||||
mManagedInstances(mUboManager.mManagedInstances),
|
|
||||||
mUbHandle(mUboManager.mUbHandle),
|
|
||||||
mAllocator(mUboManager.mAllocator) {
|
|
||||||
mEngine = Engine::Builder()
|
|
||||||
.feature("material.enable_material_instance_uniform_batching", true)
|
|
||||||
.backend(Backend::NOOP)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
mMaterial = Material::Builder()
|
|
||||||
.package(FILAMENT_TEST_RESOURCES_TEST_MATERIAL_DATA,
|
|
||||||
FILAMENT_TEST_RESOURCES_TEST_MATERIAL_SIZE)
|
|
||||||
.build(*mEngine);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TearDown() override {
|
|
||||||
mEngine->destroy(mMaterial);
|
|
||||||
Engine::destroy(&mEngine);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The engine is only for creating materials/material instances, we're not using the UboManager
|
|
||||||
// inside for testing.
|
|
||||||
Engine* mEngine = nullptr;
|
|
||||||
NiceMock<MockDriver> mMockDriver;
|
|
||||||
CommandBufferQueue mCommandBufferQueue;
|
|
||||||
CommandStream mCommandStream;
|
|
||||||
DriverApi& mDriverApi;
|
|
||||||
UboManager mUboManager;
|
|
||||||
Material const* mMaterial;
|
|
||||||
std::unordered_set<FMaterialInstance*>& mPendingInstances;
|
|
||||||
std::unordered_set<FMaterialInstance*>& mManagedInstances;
|
|
||||||
Handle<HwBufferObject>& mUbHandle;
|
|
||||||
BufferAllocator& mAllocator;
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_F(UboManagerTest, InitialState) {
|
|
||||||
EXPECT_EQ(mUboManager.getTotalSize(), DEFAULT_TOTAL_SIZE);
|
|
||||||
EXPECT_EQ(mMockDriver.nextFakeHandle, 2);
|
|
||||||
EXPECT_NE(mUbHandle.getId(), HandleBase::nullid);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(UboManagerTest, BeginFrameWithoutReallocate) {
|
|
||||||
BufferAllocator::allocation_size_t originalBufferSize = mUboManager.getTotalSize();
|
|
||||||
auto mi1 = static_cast<FMaterialInstance*>(mMaterial->createInstance());
|
|
||||||
EXPECT_EQ(mi1->getAllocationId(), BufferAllocator::UNALLOCATED);
|
|
||||||
ASSERT_TRUE(mi1->isUsingUboBatching());
|
|
||||||
|
|
||||||
// The mi1 should be put in the pending list.
|
|
||||||
mUboManager.manageMaterialInstance(mi1);
|
|
||||||
EXPECT_TRUE(mPendingInstances.contains(mi1));
|
|
||||||
EXPECT_FALSE(mManagedInstances.contains(mi1));
|
|
||||||
|
|
||||||
mUboManager.beginFrame(mDriverApi);
|
|
||||||
|
|
||||||
// The mi1 should be moved to managed list after beginFrame.
|
|
||||||
EXPECT_FALSE(mPendingInstances.contains(mi1));
|
|
||||||
EXPECT_TRUE(mManagedInstances.contains(mi1));
|
|
||||||
// Should have allocation after beginFrame.
|
|
||||||
EXPECT_TRUE(BufferAllocator::isValid(mi1->getAllocationId()));
|
|
||||||
|
|
||||||
// Reallocation is not triggered under this case.
|
|
||||||
EXPECT_EQ(mUboManager.getTotalSize(), originalBufferSize);
|
|
||||||
EXPECT_NE(mUboManager.getMemoryMappedBufferHandle().getId(), HandleBase::nullid);
|
|
||||||
|
|
||||||
mUboManager.finishBeginFrame(mDriverApi);
|
|
||||||
EXPECT_EQ(mUboManager.getMemoryMappedBufferHandle().getId(), HandleBase::nullid);
|
|
||||||
|
|
||||||
mUboManager.endFrame(mDriverApi);
|
|
||||||
EXPECT_TRUE(mAllocator.isLockedByGpu(mi1->getAllocationId()));
|
|
||||||
|
|
||||||
// We're not using the UboManager inside mEngine, so we need to unmanage the material instance
|
|
||||||
// by ourselves.
|
|
||||||
mUboManager.unmanageMaterialInstance(mi1);
|
|
||||||
EXPECT_FALSE(mPendingInstances.contains(mi1));
|
|
||||||
EXPECT_FALSE(mManagedInstances.contains(mi1));
|
|
||||||
|
|
||||||
mUboManager.terminate(mDriverApi);
|
|
||||||
mEngine->destroy(mi1);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(UboManagerTest, BeginFrameWithReallocate) {
|
|
||||||
const allocation_size_t originalBufferSize = mUboManager.getTotalSize();
|
|
||||||
const Handle<HwBufferObject> originalBufferHandle = mUbHandle;
|
|
||||||
|
|
||||||
// Create enough material instances to trigger a reallocation.
|
|
||||||
constexpr size_t numInstances = (DEFAULT_TOTAL_SIZE / DEFAULT_SLOT_SIZE) + 1;
|
|
||||||
std::vector<FMaterialInstance*> instances;
|
|
||||||
instances.reserve(numInstances);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < numInstances; ++i) {
|
|
||||||
auto mi = static_cast<FMaterialInstance*>(mMaterial->createInstance());
|
|
||||||
instances.push_back(mi);
|
|
||||||
mUboManager.manageMaterialInstance(mi);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Before beginFrame, all instances should be pending.
|
|
||||||
EXPECT_EQ(mPendingInstances.size(), numInstances);
|
|
||||||
EXPECT_TRUE(mManagedInstances.empty());
|
|
||||||
|
|
||||||
mUboManager.beginFrame(mDriverApi);
|
|
||||||
|
|
||||||
// After beginFrame, reallocation should have occurred.
|
|
||||||
EXPECT_NE(mUbHandle.getId(), originalBufferHandle.getId());
|
|
||||||
EXPECT_GT(mUboManager.getTotalSize(), originalBufferSize);
|
|
||||||
|
|
||||||
// All instances should now be managed and have valid allocations.
|
|
||||||
EXPECT_TRUE(mPendingInstances.empty());
|
|
||||||
EXPECT_EQ(mManagedInstances.size(), numInstances);
|
|
||||||
for (const auto* mi: instances) {
|
|
||||||
EXPECT_TRUE(mManagedInstances.contains(const_cast<FMaterialInstance*>(mi)));
|
|
||||||
EXPECT_TRUE(BufferAllocator::isValid(mi->getAllocationId()));
|
|
||||||
}
|
|
||||||
|
|
||||||
mUboManager.finishBeginFrame(mDriverApi);
|
|
||||||
mUboManager.terminate(mDriverApi);
|
|
||||||
|
|
||||||
for (auto* mi: instances) {
|
|
||||||
// We're not using the UboManager inside mEngine, so we need to unmanage the material instance
|
|
||||||
// by ourselves.
|
|
||||||
mUboManager.unmanageMaterialInstance(mi);
|
|
||||||
mEngine->destroy(mi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(UboManagerTest, RecycleSlot) {
|
|
||||||
auto mi1 = static_cast<FMaterialInstance*>(mMaterial->createInstance());
|
|
||||||
mUboManager.manageMaterialInstance(mi1);
|
|
||||||
|
|
||||||
// Frame 1: mi1 gets an allocation.
|
|
||||||
mUboManager.beginFrame(mDriverApi);
|
|
||||||
const AllocationId mi1AllocationId = mi1->getAllocationId();
|
|
||||||
const allocation_size_t mi1AllocationOffset =
|
|
||||||
mAllocator.getAllocationOffset(mi1AllocationId);
|
|
||||||
EXPECT_TRUE(BufferAllocator::isValid(mi1AllocationId));
|
|
||||||
mUboManager.finishBeginFrame(mDriverApi);
|
|
||||||
mUboManager.endFrame(mDriverApi); // Locks mi1's allocation.
|
|
||||||
|
|
||||||
// Now, unmanage mi1. The slot should be retired but not yet released.
|
|
||||||
mUboManager.unmanageMaterialInstance(mi1);
|
|
||||||
EXPECT_TRUE(mAllocator.isLockedByGpu(mi1AllocationId));
|
|
||||||
|
|
||||||
// Frame 2: The slot for mi1 is still locked by the GPU.
|
|
||||||
// We expect getFenceStatus to be called for the fence from frame 1.
|
|
||||||
// We'll mock it to return TIMEOUT_EXPIRED, so the resource is not reclaimed.
|
|
||||||
EXPECT_CALL(mMockDriver, getFenceStatus(_)).WillOnce(Return(FenceStatus::TIMEOUT_EXPIRED));
|
|
||||||
mUboManager.beginFrame(mDriverApi);
|
|
||||||
mUboManager.finishBeginFrame(mDriverApi);
|
|
||||||
mUboManager.endFrame(mDriverApi);
|
|
||||||
|
|
||||||
// Frame 3: Now, we'll simulate that the fence from frame 1 has signaled.
|
|
||||||
// The resource for mi1 should be reclaimed.
|
|
||||||
EXPECT_CALL(mMockDriver, getFenceStatus(_)).WillOnce(Return(FenceStatus::CONDITION_SATISFIED));
|
|
||||||
|
|
||||||
auto mi2 = static_cast<FMaterialInstance*>(mMaterial->createInstance());
|
|
||||||
mUboManager.manageMaterialInstance(mi2);
|
|
||||||
|
|
||||||
mUboManager.beginFrame(mDriverApi);
|
|
||||||
|
|
||||||
// mi2 should now have a valid allocation, and it should reuse the slot from mi1.
|
|
||||||
EXPECT_TRUE(BufferAllocator::isValid(mi2->getAllocationId()));
|
|
||||||
EXPECT_EQ(mAllocator.getAllocationOffset(mi2->getAllocationId()), mi1AllocationOffset);
|
|
||||||
|
|
||||||
mUboManager.finishBeginFrame(mDriverApi);
|
|
||||||
mUboManager.unmanageMaterialInstance(mi2);
|
|
||||||
mUboManager.terminate(mDriverApi);
|
|
||||||
|
|
||||||
mEngine->destroy(mi1);
|
|
||||||
mEngine->destroy(mi2);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(UboManagerTest, OrphanSlot) {
|
|
||||||
auto mi1 = static_cast<FMaterialInstance*>(mMaterial->createInstance());
|
|
||||||
mUboManager.manageMaterialInstance(mi1);
|
|
||||||
|
|
||||||
// Frame 1: mi1 gets an allocation.
|
|
||||||
mUboManager.beginFrame(mDriverApi);
|
|
||||||
const AllocationId alloc1 = mi1->getAllocationId();
|
|
||||||
EXPECT_TRUE(BufferAllocator::isValid(alloc1));
|
|
||||||
mUboManager.finishBeginFrame(mDriverApi);
|
|
||||||
mUboManager.endFrame(mDriverApi); // Locks alloc1.
|
|
||||||
|
|
||||||
// Frame 2: Mark the instance as dirty and begin a new frame.
|
|
||||||
// This should trigger orphaning.
|
|
||||||
mi1->getUniformBuffer().invalidate();
|
|
||||||
EXPECT_CALL(mMockDriver, getFenceStatus(_)).WillOnce(Return(FenceStatus::TIMEOUT_EXPIRED));
|
|
||||||
mUboManager.beginFrame(mDriverApi);
|
|
||||||
|
|
||||||
const AllocationId alloc2 = mi1->getAllocationId();
|
|
||||||
EXPECT_TRUE(BufferAllocator::isValid(alloc2));
|
|
||||||
EXPECT_NE(alloc1, alloc2); // Should have a new allocation.
|
|
||||||
|
|
||||||
mUboManager.finishBeginFrame(mDriverApi);
|
|
||||||
mUboManager.endFrame(mDriverApi); // Locks alloc2.
|
|
||||||
|
|
||||||
// Frame 3: The fence for alloc1 should now be signaled.
|
|
||||||
EXPECT_CALL(mMockDriver, getFenceStatus(_))
|
|
||||||
.WillOnce(Return(FenceStatus::TIMEOUT_EXPIRED)) // For alloc2's fence
|
|
||||||
.WillOnce(Return(FenceStatus::CONDITION_SATISFIED)); // For alloc1's fence
|
|
||||||
mUboManager.beginFrame(mDriverApi);
|
|
||||||
mUboManager.finishBeginFrame(mDriverApi);
|
|
||||||
mUboManager.unmanageMaterialInstance(mi1);
|
|
||||||
mUboManager.terminate(mDriverApi);
|
|
||||||
|
|
||||||
mEngine->destroy(mi1);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(UboManagerTest, DoubleManage) {
|
|
||||||
auto mi1 = static_cast<FMaterialInstance*>(mMaterial->createInstance());
|
|
||||||
mUboManager.manageMaterialInstance(mi1);
|
|
||||||
EXPECT_EQ(mPendingInstances.size(), 1);
|
|
||||||
|
|
||||||
// Managing the same instance again should be a no-op.
|
|
||||||
mUboManager.manageMaterialInstance(mi1);
|
|
||||||
EXPECT_EQ(mPendingInstances.size(), 1);
|
|
||||||
|
|
||||||
mUboManager.terminate(mDriverApi);
|
|
||||||
mEngine->destroy(mi1);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(UboManagerTest, ManageAndUnmanageBeforeBeginFrame) {
|
|
||||||
auto mi1 = static_cast<FMaterialInstance*>(mMaterial->createInstance());
|
|
||||||
mUboManager.manageMaterialInstance(mi1);
|
|
||||||
EXPECT_TRUE(mPendingInstances.contains(mi1));
|
|
||||||
|
|
||||||
mUboManager.unmanageMaterialInstance(mi1);
|
|
||||||
EXPECT_FALSE(mPendingInstances.contains(mi1));
|
|
||||||
|
|
||||||
// After beginFrame, the instance should not be in any list.
|
|
||||||
mUboManager.beginFrame(mDriverApi);
|
|
||||||
EXPECT_FALSE(mPendingInstances.contains(mi1));
|
|
||||||
EXPECT_FALSE(mManagedInstances.contains(mi1));
|
|
||||||
EXPECT_EQ(mi1->getAllocationId(), BufferAllocator::UNALLOCATED);
|
|
||||||
|
|
||||||
mUboManager.terminate(mDriverApi);
|
|
||||||
mEngine->destroy(mi1);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(UboManagerTest, UnmanageUnmanaged) {
|
|
||||||
auto mi1 = static_cast<FMaterialInstance*>(mMaterial->createInstance());
|
|
||||||
|
|
||||||
// Unmanaging an instance that was never managed should not cause any issues.
|
|
||||||
mUboManager.unmanageMaterialInstance(mi1);
|
|
||||||
EXPECT_FALSE(mPendingInstances.contains(mi1));
|
|
||||||
EXPECT_FALSE(mManagedInstances.contains(mi1));
|
|
||||||
|
|
||||||
mUboManager.terminate(mDriverApi);
|
|
||||||
mEngine->destroy(mi1);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(UboManagerTest, AllAllocationsLockedAfterEndFrame) {
|
|
||||||
constexpr size_t numInstances = 5;
|
|
||||||
std::vector<FMaterialInstance*> instances;
|
|
||||||
instances.reserve(numInstances);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < numInstances; ++i) {
|
|
||||||
auto mi = static_cast<FMaterialInstance*>(mMaterial->createInstance());
|
|
||||||
instances.push_back(mi);
|
|
||||||
mUboManager.manageMaterialInstance(mi);
|
|
||||||
}
|
|
||||||
|
|
||||||
mUboManager.beginFrame(mDriverApi);
|
|
||||||
mUboManager.finishBeginFrame(mDriverApi);
|
|
||||||
mUboManager.endFrame(mDriverApi);
|
|
||||||
|
|
||||||
for (const auto* mi: instances) {
|
|
||||||
EXPECT_TRUE(mAllocator.isLockedByGpu(mi->getAllocationId()));
|
|
||||||
}
|
|
||||||
|
|
||||||
mUboManager.terminate(mDriverApi);
|
|
||||||
for (auto* mi: instances) {
|
|
||||||
// We're not using the UboManager inside mEngine, so we need to unmanage the material instance
|
|
||||||
// by ourselves.
|
|
||||||
mUboManager.unmanageMaterialInstance(mi);
|
|
||||||
mEngine->destroy(mi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(UboManagerTest, AllAllocationsLockedAfterEndFrameWithInvalidIdInBetween) {
|
|
||||||
constexpr size_t numInstances = 5;
|
|
||||||
std::vector<FMaterialInstance*> instances;
|
|
||||||
instances.reserve(numInstances);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < numInstances; ++i) {
|
|
||||||
auto mi = static_cast<FMaterialInstance*>(mMaterial->createInstance());
|
|
||||||
instances.push_back(mi);
|
|
||||||
mUboManager.manageMaterialInstance(mi);
|
|
||||||
}
|
|
||||||
|
|
||||||
mUboManager.beginFrame(mDriverApi);
|
|
||||||
mUboManager.finishBeginFrame(mDriverApi);
|
|
||||||
|
|
||||||
// It should rarely happen, but we want to make sure all other instances are locked properly.
|
|
||||||
instances[2]->assignUboAllocation(mUbHandle, BufferAllocator::REALLOCATION_REQUIRED, 0);
|
|
||||||
mUboManager.endFrame(mDriverApi);
|
|
||||||
|
|
||||||
for (const auto* mi: instances) {
|
|
||||||
if (BufferAllocator::isValid(mi->getAllocationId())) {
|
|
||||||
EXPECT_TRUE(mAllocator.isLockedByGpu(mi->getAllocationId()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mUboManager.terminate(mDriverApi);
|
|
||||||
for (auto* mi: instances) {
|
|
||||||
// We're not using the UboManager inside mEngine, so we need to unmanage the material instance
|
|
||||||
// by ourselves.
|
|
||||||
mUboManager.unmanageMaterialInstance(mi);
|
|
||||||
mEngine->destroy(mi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Add more tests for the beginFrame flow
|
|
||||||
Binary file not shown.
Binary file not shown.
@@ -1,12 +1,12 @@
|
|||||||
Pod::Spec.new do |spec|
|
Pod::Spec.new do |spec|
|
||||||
spec.name = "Filament"
|
spec.name = "Filament"
|
||||||
spec.version = "1.67.1"
|
spec.version = "1.67.0"
|
||||||
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
|
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
|
||||||
spec.homepage = "https://google.github.io/filament"
|
spec.homepage = "https://google.github.io/filament"
|
||||||
spec.authors = "Google LLC."
|
spec.authors = "Google LLC."
|
||||||
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
|
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
|
||||||
spec.platform = :ios, "11.0"
|
spec.platform = :ios, "11.0"
|
||||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.67.1/filament-v1.67.1-ios.tgz" }
|
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.67.0/filament-v1.67.0-ios.tgz" }
|
||||||
|
|
||||||
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
|
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
|
||||||
spec.pod_target_xcconfig = {
|
spec.pod_target_xcconfig = {
|
||||||
|
|||||||
@@ -56,9 +56,6 @@ public:
|
|||||||
//! Returns true if the map is full.
|
//! Returns true if the map is full.
|
||||||
bool full() const noexcept { return mSize == N; }
|
bool full() const noexcept { return mSize == N; }
|
||||||
|
|
||||||
//! Clears the map entirely.
|
|
||||||
void clear() noexcept { mSize = 0; mHead = 0; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts a new key-value pair.
|
* Inserts a new key-value pair.
|
||||||
* The key must be greater than the key of the last inserted element.
|
* The key must be greater than the key of the last inserted element.
|
||||||
@@ -68,7 +65,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
UTILS_NOINLINE void insert(key_type key, mapped_type value) {
|
UTILS_NOINLINE void insert(key_type key, mapped_type value) {
|
||||||
assert(empty() || key > back().first); // assert monotonic
|
assert(empty() || key > back().first); // assert monotonic
|
||||||
if (UTILS_LIKELY(full())) {
|
if (full()) {
|
||||||
// container is full, replace the oldest element
|
// container is full, replace the oldest element
|
||||||
mStorage[mHead] = { key, value };
|
mStorage[mHead] = { key, value };
|
||||||
mHead = (mHead + 1) % N;
|
mHead = (mHead + 1) % N;
|
||||||
|
|||||||
@@ -302,16 +302,14 @@ vec4 evaluateLights(const MaterialInputs material) {
|
|||||||
return vec4(color, computeDiffuseAlpha(material.baseColor.a));
|
return vec4(color, computeDiffuseAlpha(material.baseColor.a));
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 emissive(const MaterialInputs material, float alpha) {
|
void addEmissive(const MaterialInputs material, inout vec4 color) {
|
||||||
#if defined(MATERIAL_HAS_EMISSIVE)
|
#if defined(MATERIAL_HAS_EMISSIVE)
|
||||||
highp vec4 emissive = material.emissive;
|
highp vec4 emissive = material.emissive;
|
||||||
highp float attenuation = mix(1.0, getExposure(), emissive.w);
|
highp float attenuation = mix(1.0, getExposure(), emissive.w);
|
||||||
#if defined(BLEND_MODE_TRANSPARENT) || defined(BLEND_MODE_FADE)
|
#if defined(BLEND_MODE_TRANSPARENT) || defined(BLEND_MODE_FADE)
|
||||||
attenuation *= alpha;
|
attenuation *= color.a;
|
||||||
#endif
|
#endif
|
||||||
return emissive.rgb * attenuation;
|
color.rgb += emissive.rgb * attenuation;
|
||||||
#else
|
|
||||||
return vec3(0.0);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,7 +321,7 @@ vec3 emissive(const MaterialInputs material, float alpha) {
|
|||||||
*/
|
*/
|
||||||
vec4 evaluateMaterial(const MaterialInputs material) {
|
vec4 evaluateMaterial(const MaterialInputs material) {
|
||||||
vec4 color = evaluateLights(material);
|
vec4 color = evaluateLights(material);
|
||||||
color.rgb += emissive(material, color.a);
|
addEmissive(material, color);
|
||||||
// kill +inf generated by strong highlights
|
// kill +inf generated by strong highlights
|
||||||
color.rgb = min(color.rgb, MEDIUMP_FLT_MAX);
|
color.rgb = min(color.rgb, MEDIUMP_FLT_MAX);
|
||||||
return color;
|
return color;
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
Language: Cpp
|
Language: Cpp
|
||||||
BasedOnStyle: Google
|
BasedOnStyle: Google
|
||||||
PointerAlignment: Left
|
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|||||||
Vendored
-37
@@ -1,37 +0,0 @@
|
|||||||
---
|
|
||||||
Checks: >
|
|
||||||
abseil-*,
|
|
||||||
bugprone-*,
|
|
||||||
clang-analyzer-*,
|
|
||||||
cppcoreguidelines-*,
|
|
||||||
google-*,
|
|
||||||
misc-*,
|
|
||||||
performance-*,
|
|
||||||
readability-*,
|
|
||||||
-clang-analyzer-deadcode*,
|
|
||||||
-clang-analyzer-optin*,
|
|
||||||
-readability-identifier-length
|
|
||||||
WarningsAsErrors: ''
|
|
||||||
HeaderFilterRegex: ''
|
|
||||||
FormatStyle: none
|
|
||||||
CheckOptions:
|
|
||||||
llvm-else-after-return.WarnOnConditionVariables: 'false'
|
|
||||||
modernize-loop-convert.MinConfidence: reasonable
|
|
||||||
modernize-replace-auto-ptr.IncludeStyle: llvm
|
|
||||||
cert-str34-c.DiagnoseSignedUnsignedCharComparisons: 'false'
|
|
||||||
google-readability-namespace-comments.ShortNamespaceLines: '10'
|
|
||||||
cert-err33-c.CheckedFunctions: '::aligned_alloc;::asctime_s;::at_quick_exit;::atexit;::bsearch;::bsearch_s;::btowc;::c16rtomb;::c32rtomb;::calloc;::clock;::cnd_broadcast;::cnd_init;::cnd_signal;::cnd_timedwait;::cnd_wait;::ctime_s;::fclose;::fflush;::fgetc;::fgetpos;::fgets;::fgetwc;::fopen;::fopen_s;::fprintf;::fprintf_s;::fputc;::fputs;::fputwc;::fputws;::fread;::freopen;::freopen_s;::fscanf;::fscanf_s;::fseek;::fsetpos;::ftell;::fwprintf;::fwprintf_s;::fwrite;::fwscanf;::fwscanf_s;::getc;::getchar;::getenv;::getenv_s;::gets_s;::getwc;::getwchar;::gmtime;::gmtime_s;::localtime;::localtime_s;::malloc;::mbrtoc16;::mbrtoc32;::mbsrtowcs;::mbsrtowcs_s;::mbstowcs;::mbstowcs_s;::memchr;::mktime;::mtx_init;::mtx_lock;::mtx_timedlock;::mtx_trylock;::mtx_unlock;::printf_s;::putc;::putwc;::raise;::realloc;::remove;::rename;::scanf;::scanf_s;::setlocale;::setvbuf;::signal;::snprintf;::snprintf_s;::sprintf;::sprintf_s;::sscanf;::sscanf_s;::strchr;::strerror_s;::strftime;::strpbrk;::strrchr;::strstr;::strtod;::strtof;::strtoimax;::strtok;::strtok_s;::strtol;::strtold;::strtoll;::strtoul;::strtoull;::strtoumax;::strxfrm;::swprintf;::swprintf_s;::swscanf;::swscanf_s;::thrd_create;::thrd_detach;::thrd_join;::thrd_sleep;::time;::timespec_get;::tmpfile;::tmpfile_s;::tmpnam;::tmpnam_s;::tss_create;::tss_get;::tss_set;::ungetc;::ungetwc;::vfprintf;::vfprintf_s;::vfscanf;::vfscanf_s;::vfwprintf;::vfwprintf_s;::vfwscanf;::vfwscanf_s;::vprintf_s;::vscanf;::vscanf_s;::vsnprintf;::vsnprintf_s;::vsprintf;::vsprintf_s;::vsscanf;::vsscanf_s;::vswprintf;::vswprintf_s;::vswscanf;::vswscanf_s;::vwprintf_s;::vwscanf;::vwscanf_s;::wcrtomb;::wcschr;::wcsftime;::wcspbrk;::wcsrchr;::wcsrtombs;::wcsrtombs_s;::wcsstr;::wcstod;::wcstof;::wcstoimax;::wcstok;::wcstok_s;::wcstol;::wcstold;::wcstoll;::wcstombs;::wcstombs_s;::wcstoul;::wcstoull;::wcstoumax;::wcsxfrm;::wctob;::wctrans;::wctype;::wmemchr;::wprintf_s;::wscanf;::wscanf_s;'
|
|
||||||
cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField: 'false'
|
|
||||||
cert-dcl16-c.NewSuffixes: 'L;LL;LU;LLU'
|
|
||||||
google-readability-braces-around-statements.ShortStatementLines: '1'
|
|
||||||
cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic: 'true'
|
|
||||||
google-readability-namespace-comments.SpacesBeforeComments: '2'
|
|
||||||
modernize-loop-convert.MaxCopySize: '16'
|
|
||||||
modernize-pass-by-value.IncludeStyle: llvm
|
|
||||||
modernize-use-nullptr.NullMacros: 'NULL'
|
|
||||||
llvm-qualified-auto.AddConstToQualified: 'false'
|
|
||||||
modernize-loop-convert.NamingStyle: CamelCase
|
|
||||||
llvm-else-after-return.WarnOnUnfixable: 'false'
|
|
||||||
google-readability-function-size.StatementThreshold: '800'
|
|
||||||
...
|
|
||||||
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
.*third_party/.*
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: Create a report to help us improve
|
|
||||||
title: "[BUG]"
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Describe the bug**
|
|
||||||
A clear and concise description of what the bug is.
|
|
||||||
|
|
||||||
**System**
|
|
||||||
Which OS, compiler, and compiler version are you using:
|
|
||||||
- OS:
|
|
||||||
- Compiler and version:
|
|
||||||
|
|
||||||
**To reproduce**
|
|
||||||
Steps to reproduce the behavior:
|
|
||||||
1. sync to commit ...
|
|
||||||
2. cmake/bazel...
|
|
||||||
3. make ...
|
|
||||||
4. See error
|
|
||||||
|
|
||||||
**Expected behavior**
|
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
|
|
||||||
**Screenshots**
|
|
||||||
If applicable, add screenshots to help explain your problem.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context about the problem here.
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: Suggest an idea for this project
|
|
||||||
title: "[FR]"
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
|
||||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
|
||||||
A clear and concise description of what you want to happen.
|
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
|
||||||
A clear and concise description of any alternative solutions or features you've considered.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context or screenshots about the feature request here.
|
|
||||||
-12
@@ -1,12 +0,0 @@
|
|||||||
if ! bazel version; then
|
|
||||||
arch=$(uname -m)
|
|
||||||
if [ "$arch" == "aarch64" ]; then
|
|
||||||
arch="arm64"
|
|
||||||
fi
|
|
||||||
echo "Downloading $arch Bazel binary from GitHub releases."
|
|
||||||
curl -L -o $HOME/bin/bazel --create-dirs "https://github.com/bazelbuild/bazel/releases/download/8.2.0/bazel-8.2.0-linux-$arch"
|
|
||||||
chmod +x $HOME/bin/bazel
|
|
||||||
else
|
|
||||||
# Bazel is installed for the correct architecture
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
-35
@@ -1,35 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Checkout LLVM sources
|
|
||||||
git clone --filter=blob:none --depth=1 --branch llvmorg-19.1.6 --no-checkout https://github.com/llvm/llvm-project.git llvm-project
|
|
||||||
cd llvm-project
|
|
||||||
git sparse-checkout set --cone
|
|
||||||
git checkout llvmorg-19.1.6
|
|
||||||
git sparse-checkout set cmake llvm/cmake runtimes libcxx libcxxabi
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
## Setup libc++ options
|
|
||||||
if [ -z "$BUILD_32_BITS" ]; then
|
|
||||||
export BUILD_32_BITS=OFF && echo disabling 32 bit build
|
|
||||||
fi
|
|
||||||
|
|
||||||
## Build and install libc++ (Use unstable ABI for better sanitizer coverage)
|
|
||||||
mkdir llvm-build && cd llvm-build
|
|
||||||
cmake -GNinja \
|
|
||||||
-DCMAKE_C_COMPILER=${CC} \
|
|
||||||
-DCMAKE_CXX_COMPILER=${CXX} \
|
|
||||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
|
||||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
|
||||||
-DLIBCXX_ABI_UNSTABLE=OFF \
|
|
||||||
-DLLVM_USE_SANITIZER=${LIBCXX_SANITIZER} \
|
|
||||||
-DLLVM_BUILD_32_BITS=${BUILD_32_BITS} \
|
|
||||||
-DLIBCXXABI_USE_LLVM_UNWINDER=OFF \
|
|
||||||
-DLLVM_INCLUDE_TESTS=OFF \
|
|
||||||
-DLIBCXX_INCLUDE_TESTS=OFF \
|
|
||||||
-DLIBCXX_INCLUDE_BENCHMARKS=OFF \
|
|
||||||
-DLLVM_ENABLE_RUNTIMES='libcxx;libcxxabi' \
|
|
||||||
../llvm-project/runtimes/
|
|
||||||
cmake --build . -- cxx cxxabi
|
|
||||||
cd ..
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
name: bazel
|
|
||||||
|
|
||||||
on:
|
|
||||||
push: {}
|
|
||||||
pull_request: {}
|
|
||||||
|
|
||||||
env:
|
|
||||||
CMAKE_GENERATOR: Ninja
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build_and_test_default:
|
|
||||||
name: bazel.${{ matrix.os }}
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: mount bazel cache
|
|
||||||
uses: actions/cache@v4
|
|
||||||
env:
|
|
||||||
cache-name: bazel-cache
|
|
||||||
with:
|
|
||||||
path: "~/.cache/bazel"
|
|
||||||
key: ${{ env.cache-name }}-${{ matrix.os }}-${{ github.ref }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ env.cache-name }}-${{ matrix.os }}-main
|
|
||||||
|
|
||||||
- name: build
|
|
||||||
run: |
|
|
||||||
bazel build //:benchmark //:benchmark_main //test/...
|
|
||||||
|
|
||||||
- name: test
|
|
||||||
run: |
|
|
||||||
bazel test --test_output=all //test/...
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
name: build-and-test-min-cmake
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ main ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ main ]
|
|
||||||
|
|
||||||
env:
|
|
||||||
CMAKE_GENERATOR: Ninja
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
job:
|
|
||||||
name: ${{ matrix.os }}.min-cmake
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-latest, macos-latest]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- uses: lukka/get-cmake@latest
|
|
||||||
with:
|
|
||||||
cmakeVersion: 3.13.0
|
|
||||||
|
|
||||||
- name: create build environment
|
|
||||||
run: cmake -E make_directory ${{ runner.workspace }}/_build
|
|
||||||
|
|
||||||
- name: setup cmake initial cache
|
|
||||||
run: touch compiler-cache.cmake
|
|
||||||
|
|
||||||
- name: configure cmake
|
|
||||||
env:
|
|
||||||
CXX: ${{ matrix.compiler }}
|
|
||||||
shell: bash
|
|
||||||
working-directory: ${{ runner.workspace }}/_build
|
|
||||||
run: >
|
|
||||||
cmake -C ${{ github.workspace }}/compiler-cache.cmake
|
|
||||||
$GITHUB_WORKSPACE
|
|
||||||
-DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON
|
|
||||||
-DCMAKE_CXX_VISIBILITY_PRESET=hidden
|
|
||||||
-DCMAKE_VISIBILITY_INLINES_HIDDEN=ON
|
|
||||||
|
|
||||||
- name: build
|
|
||||||
shell: bash
|
|
||||||
working-directory: ${{ runner.workspace }}/_build
|
|
||||||
run: cmake --build .
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
name: build-and-test-perfcounters
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ main ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ main ]
|
|
||||||
|
|
||||||
env:
|
|
||||||
CMAKE_GENERATOR: Ninja
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
job:
|
|
||||||
# TODO(dominic): Extend this to include compiler and set through env: CC/CXX.
|
|
||||||
name: ${{ matrix.os }}.${{ matrix.build_type }}
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-latest]
|
|
||||||
build_type: ['Release', 'Debug']
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: install libpfm
|
|
||||||
run: |
|
|
||||||
sudo apt update
|
|
||||||
sudo apt -y install libpfm4-dev
|
|
||||||
|
|
||||||
- name: create build environment
|
|
||||||
run: cmake -E make_directory ${{ runner.workspace }}/_build
|
|
||||||
|
|
||||||
- name: configure cmake
|
|
||||||
shell: bash
|
|
||||||
working-directory: ${{ runner.workspace }}/_build
|
|
||||||
run: >
|
|
||||||
cmake $GITHUB_WORKSPACE
|
|
||||||
-DBENCHMARK_ENABLE_LIBPFM=1
|
|
||||||
-DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON
|
|
||||||
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
|
||||||
|
|
||||||
- name: build
|
|
||||||
shell: bash
|
|
||||||
working-directory: ${{ runner.workspace }}/_build
|
|
||||||
run: cmake --build . --config ${{ matrix.build_type }}
|
|
||||||
|
|
||||||
# Skip testing, for now. It seems perf_event_open does not succeed on the
|
|
||||||
# hosting machine, very likely a permissions issue.
|
|
||||||
# TODO(mtrofin): Enable test.
|
|
||||||
# - name: test
|
|
||||||
# shell: bash
|
|
||||||
# working-directory: ${{ runner.workspace }}/_build
|
|
||||||
# run: ctest -C ${{ matrix.build_type }} --rerun-failed --output-on-failure
|
|
||||||
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
name: build-and-test
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ main ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ main ]
|
|
||||||
|
|
||||||
env:
|
|
||||||
CMAKE_GENERATOR: Ninja
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
# TODO: add 32-bit builds (g++ and clang++) for ubuntu
|
|
||||||
# (requires g++-multilib and libc6:i386)
|
|
||||||
# TODO: add coverage build (requires lcov)
|
|
||||||
# TODO: add clang + libc++ builds for ubuntu
|
|
||||||
job:
|
|
||||||
name: ${{ matrix.os }}.${{ matrix.build_type }}.${{ matrix.lib }}.${{ matrix.compiler }}
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-24.04, ubuntu-22.04, ubuntu-24.04-arm, macos-latest]
|
|
||||||
build_type: ['Release', 'Debug']
|
|
||||||
compiler: ['g++', 'clang++']
|
|
||||||
lib: ['shared', 'static']
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Install dependencies (macos)
|
|
||||||
if: runner.os == 'macOS'
|
|
||||||
run: brew install ninja
|
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: build
|
|
||||||
uses: threeal/cmake-action@v2.1.0
|
|
||||||
with:
|
|
||||||
build-dir: ${{ runner.workspace }}/_build
|
|
||||||
cxx-compiler: ${{ matrix.compiler }}
|
|
||||||
options: |
|
|
||||||
BENCHMARK_DOWNLOAD_DEPENDENCIES=ON
|
|
||||||
BUILD_SHARED_LIBS=${{ matrix.lib == 'shared' }}
|
|
||||||
CMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
|
||||||
CMAKE_CXX_COMPILER=${{ matrix.compiler }}
|
|
||||||
CMAKE_CXX_VISIBILITY_PRESET=hidden
|
|
||||||
CMAKE_VISIBILITY_INLINES_HIDDEN=ON
|
|
||||||
|
|
||||||
- name: test
|
|
||||||
shell: bash
|
|
||||||
working-directory: ${{ runner.workspace }}/_build
|
|
||||||
run: ctest -C ${{ matrix.build_type }} -VV
|
|
||||||
|
|
||||||
msvc:
|
|
||||||
name: ${{ matrix.os }}.${{ matrix.build_type }}.${{ matrix.lib }}.${{ matrix.msvc }}
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
shell: powershell
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
msvc:
|
|
||||||
- VS-16-2019
|
|
||||||
- VS-17-2022
|
|
||||||
build_type:
|
|
||||||
- Debug
|
|
||||||
- Release
|
|
||||||
lib:
|
|
||||||
- shared
|
|
||||||
- static
|
|
||||||
include:
|
|
||||||
- msvc: VS-16-2019
|
|
||||||
os: windows-2019
|
|
||||||
generator: 'Visual Studio 16 2019'
|
|
||||||
- msvc: VS-17-2022
|
|
||||||
os: windows-2022
|
|
||||||
generator: 'Visual Studio 17 2022'
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- uses: lukka/get-cmake@latest
|
|
||||||
|
|
||||||
- name: configure cmake
|
|
||||||
run: >
|
|
||||||
cmake -S . -B ${{ runner.workspace }}/_build/
|
|
||||||
-G "${{ matrix.generator }}"
|
|
||||||
-DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON
|
|
||||||
-DBUILD_SHARED_LIBS=${{ matrix.lib == 'shared' }}
|
|
||||||
|
|
||||||
- name: build
|
|
||||||
run: cmake --build ${{ runner.workspace }}/_build/ --config ${{ matrix.build_type }}
|
|
||||||
|
|
||||||
- name: test
|
|
||||||
run: ctest --test-dir ${{ runner.workspace }}/_build/ -C ${{ matrix.build_type }} -VV
|
|
||||||
|
|
||||||
msys2:
|
|
||||||
name: ${{ matrix.os }}.${{ matrix.build_type }}.${{ matrix.lib }}.${{ matrix.msys2.msystem }}
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
shell: msys2 {0}
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
os: [ windows-latest ]
|
|
||||||
msys2:
|
|
||||||
- { msystem: MINGW64, arch: x86_64, family: GNU, compiler: g++ }
|
|
||||||
- { msystem: CLANG64, arch: x86_64, family: LLVM, compiler: clang++ }
|
|
||||||
- { msystem: UCRT64, arch: x86_64, family: GNU, compiler: g++ }
|
|
||||||
build_type:
|
|
||||||
- Debug
|
|
||||||
- Release
|
|
||||||
lib:
|
|
||||||
- shared
|
|
||||||
- static
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: setup msys2
|
|
||||||
uses: msys2/setup-msys2@v2
|
|
||||||
with:
|
|
||||||
cache: false
|
|
||||||
msystem: ${{ matrix.msys2.msystem }}
|
|
||||||
update: true
|
|
||||||
install: >-
|
|
||||||
git
|
|
||||||
base-devel
|
|
||||||
pacboy: >-
|
|
||||||
gcc:p
|
|
||||||
clang:p
|
|
||||||
cmake:p
|
|
||||||
ninja:p
|
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
# NOTE: we can't use cmake actions here as we need to do everything in msys2 shell.
|
|
||||||
- name: configure cmake
|
|
||||||
env:
|
|
||||||
CXX: ${{ matrix.msys2.compiler }}
|
|
||||||
run: >
|
|
||||||
cmake -S . -B _build/
|
|
||||||
-GNinja
|
|
||||||
-DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON
|
|
||||||
-DBUILD_SHARED_LIBS=${{ matrix.lib == 'shared' }}
|
|
||||||
|
|
||||||
- name: build
|
|
||||||
run: cmake --build _build/ --config ${{ matrix.build_type }}
|
|
||||||
|
|
||||||
- name: test
|
|
||||||
working-directory: _build
|
|
||||||
run: ctest -C ${{ matrix.build_type }} -VV
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
name: clang-format-lint
|
|
||||||
on:
|
|
||||||
push: {}
|
|
||||||
pull_request: {}
|
|
||||||
|
|
||||||
env:
|
|
||||||
CMAKE_GENERATOR: Ninja
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
job:
|
|
||||||
name: check-clang-format
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: DoozyX/clang-format-lint-action@v0.18.2
|
|
||||||
with:
|
|
||||||
source: './include/benchmark ./src ./test'
|
|
||||||
clangFormatVersion: 18
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
name: clang-tidy
|
|
||||||
|
|
||||||
on:
|
|
||||||
push: {}
|
|
||||||
pull_request: {}
|
|
||||||
|
|
||||||
env:
|
|
||||||
CMAKE_GENERATOR: Ninja
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
job:
|
|
||||||
name: run-clang-tidy
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: install clang-tidy
|
|
||||||
run: sudo apt update && sudo apt -y install clang-tidy
|
|
||||||
|
|
||||||
- name: create build environment
|
|
||||||
run: cmake -E make_directory ${{ github.workspace }}/_build
|
|
||||||
|
|
||||||
- name: configure cmake
|
|
||||||
shell: bash
|
|
||||||
working-directory: ${{ github.workspace }}/_build
|
|
||||||
run: >
|
|
||||||
cmake $GITHUB_WORKSPACE
|
|
||||||
-DBENCHMARK_ENABLE_ASSEMBLY_TESTS=OFF
|
|
||||||
-DBENCHMARK_ENABLE_LIBPFM=OFF
|
|
||||||
-DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON
|
|
||||||
-DCMAKE_C_COMPILER=clang
|
|
||||||
-DCMAKE_CXX_COMPILER=clang++
|
|
||||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
|
||||||
-DGTEST_COMPILE_COMMANDS=OFF
|
|
||||||
|
|
||||||
- name: run
|
|
||||||
shell: bash
|
|
||||||
working-directory: ${{ github.workspace }}/_build
|
|
||||||
run: run-clang-tidy -config-file=$GITHUB_WORKSPACE/.clang-tidy
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
name: doxygen
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
pull_request:
|
|
||||||
branches: [main]
|
|
||||||
|
|
||||||
env:
|
|
||||||
CMAKE_GENERATOR: Ninja
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-and-deploy:
|
|
||||||
name: Build HTML documentation
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Fetching sources
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Installing build dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt update
|
|
||||||
sudo apt install doxygen gcc git
|
|
||||||
|
|
||||||
- name: Creating build directory
|
|
||||||
run: mkdir build
|
|
||||||
|
|
||||||
- name: Building HTML documentation with Doxygen
|
|
||||||
run: |
|
|
||||||
cmake -S . -B build -DBENCHMARK_ENABLE_TESTING:BOOL=OFF -DBENCHMARK_ENABLE_DOXYGEN:BOOL=ON -DBENCHMARK_INSTALL_DOCS:BOOL=ON
|
|
||||||
cmake --build build --target benchmark_doxygen
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
name: python + Bazel pre-commit checks
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ main ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ main ]
|
|
||||||
|
|
||||||
env:
|
|
||||||
CMAKE_GENERATOR: Ninja
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
pre-commit:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
MYPY_CACHE_DIR: "${{ github.workspace }}/.cache/mypy"
|
|
||||||
RUFF_CACHE_DIR: "${{ github.workspace }}/.cache/ruff"
|
|
||||||
PRE_COMMIT_HOME: "${{ github.workspace }}/.cache/pre-commit"
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: 3.11
|
|
||||||
cache: pip
|
|
||||||
cache-dependency-path: pyproject.toml
|
|
||||||
- name: Install dependencies
|
|
||||||
run: python -m pip install ".[dev]"
|
|
||||||
- name: Cache pre-commit tools
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
${{ env.MYPY_CACHE_DIR }}
|
|
||||||
${{ env.RUFF_CACHE_DIR }}
|
|
||||||
${{ env.PRE_COMMIT_HOME }}
|
|
||||||
key: ${{ runner.os }}-${{ hashFiles('.pre-commit-config.yaml') }}-linter-cache
|
|
||||||
- name: Run pre-commit checks
|
|
||||||
run: pre-commit run --all-files --verbose --show-diff-on-failure
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
name: sanitizer
|
|
||||||
|
|
||||||
on:
|
|
||||||
push: {}
|
|
||||||
pull_request: {}
|
|
||||||
|
|
||||||
env:
|
|
||||||
CMAKE_GENERATOR: Ninja
|
|
||||||
UBSAN_OPTIONS: "print_stacktrace=1"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
job:
|
|
||||||
name: ${{ matrix.sanitizer }}.${{ matrix.build_type }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
build_type: ['Debug', 'RelWithDebInfo']
|
|
||||||
sanitizer: ['asan', 'ubsan', 'tsan', 'msan']
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: configure msan env
|
|
||||||
if: matrix.sanitizer == 'msan'
|
|
||||||
run: |
|
|
||||||
echo "EXTRA_FLAGS=-g -O2 -fno-omit-frame-pointer -fsanitize=memory -fsanitize-memory-track-origins" >> $GITHUB_ENV
|
|
||||||
echo "LIBCXX_SANITIZER=MemoryWithOrigins" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: configure ubsan env
|
|
||||||
if: matrix.sanitizer == 'ubsan'
|
|
||||||
run: |
|
|
||||||
echo "EXTRA_FLAGS=-g -O2 -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=all" >> $GITHUB_ENV
|
|
||||||
echo "LIBCXX_SANITIZER=Undefined" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: configure asan env
|
|
||||||
if: matrix.sanitizer == 'asan'
|
|
||||||
run: |
|
|
||||||
echo "EXTRA_FLAGS=-g -O2 -fno-omit-frame-pointer -fsanitize=address -fno-sanitize-recover=all" >> $GITHUB_ENV
|
|
||||||
echo "LIBCXX_SANITIZER=Address" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: configure tsan env
|
|
||||||
if: matrix.sanitizer == 'tsan'
|
|
||||||
run: |
|
|
||||||
echo "EXTRA_FLAGS=-g -O2 -fno-omit-frame-pointer -fsanitize=thread -fno-sanitize-recover=all" >> $GITHUB_ENV
|
|
||||||
echo "LIBCXX_SANITIZER=Thread" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: fine-tune asan options
|
|
||||||
# in asan we get an error from std::regex. ignore it.
|
|
||||||
if: matrix.sanitizer == 'asan'
|
|
||||||
run: |
|
|
||||||
echo "ASAN_OPTIONS=alloc_dealloc_mismatch=0" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: setup clang
|
|
||||||
uses: egor-tensin/setup-clang@v1
|
|
||||||
with:
|
|
||||||
version: latest
|
|
||||||
platform: x64
|
|
||||||
|
|
||||||
- name: configure clang
|
|
||||||
run: |
|
|
||||||
echo "CC=cc" >> $GITHUB_ENV
|
|
||||||
echo "CXX=c++" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: build libc++ (non-asan)
|
|
||||||
if: matrix.sanitizer != 'asan'
|
|
||||||
run: |
|
|
||||||
"${GITHUB_WORKSPACE}/.github/libcxx-setup.sh"
|
|
||||||
echo "EXTRA_CXX_FLAGS=-stdlib=libc++ -L${GITHUB_WORKSPACE}/llvm-build/lib -lc++abi -I${GITHUB_WORKSPACE}/llvm-build/include/c++/v1 -Isystem${GITHUB_WORKSPACE}/llvm-build/include/c++/v1 -Wl,-rpath,${GITHUB_WORKSPACE}/llvm-build/lib" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: create build environment
|
|
||||||
run: cmake -E make_directory ${{ runner.workspace }}/_build
|
|
||||||
|
|
||||||
- name: configure cmake
|
|
||||||
shell: bash
|
|
||||||
working-directory: ${{ runner.workspace }}/_build
|
|
||||||
run: >
|
|
||||||
VERBOSE=1
|
|
||||||
cmake -GNinja $GITHUB_WORKSPACE
|
|
||||||
-DBENCHMARK_ENABLE_ASSEMBLY_TESTS=OFF
|
|
||||||
-DBENCHMARK_ENABLE_LIBPFM=OFF
|
|
||||||
-DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON
|
|
||||||
-DCMAKE_C_COMPILER=${{ env.CC }}
|
|
||||||
-DCMAKE_CXX_COMPILER=${{ env.CXX }}
|
|
||||||
-DCMAKE_C_FLAGS="${{ env.EXTRA_FLAGS }}"
|
|
||||||
-DCMAKE_CXX_FLAGS="${{ env.EXTRA_FLAGS }} ${{ env.EXTRA_CXX_FLAGS }}"
|
|
||||||
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
|
||||||
|
|
||||||
- name: build
|
|
||||||
shell: bash
|
|
||||||
working-directory: ${{ runner.workspace }}/_build
|
|
||||||
run: cmake --build . --config ${{ matrix.build_type }}
|
|
||||||
|
|
||||||
- name: test
|
|
||||||
shell: bash
|
|
||||||
working-directory: ${{ runner.workspace }}/_build
|
|
||||||
run: ctest -C ${{ matrix.build_type }} -VV
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
name: test-bindings
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
pull_request:
|
|
||||||
branches: [main]
|
|
||||||
|
|
||||||
env:
|
|
||||||
CMAKE_GENERATOR: Ninja
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
python_bindings:
|
|
||||||
name: Test GBM Python ${{ matrix.python-version }} bindings on ${{ matrix.os }}
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
os: [ ubuntu-latest, macos-latest, windows-latest ]
|
|
||||||
python-version: [ "3.10", "3.11", "3.12", "3.13" ]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
|
||||||
uses: actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: ${{ matrix.python-version }}
|
|
||||||
- name: Install GBM Python bindings on ${{ matrix.os }}
|
|
||||||
run: python -m pip install .
|
|
||||||
- name: Run example on ${{ matrix.os }} under Python ${{ matrix.python-version }}
|
|
||||||
run: python bindings/python/google_benchmark/example.py
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
name: Build and upload Python wheels
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
release:
|
|
||||||
types:
|
|
||||||
- published
|
|
||||||
|
|
||||||
env:
|
|
||||||
CMAKE_GENERATOR: Ninja
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build_sdist:
|
|
||||||
name: Build source distribution
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Check out repo
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Install Python 3.12
|
|
||||||
uses: actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: "3.12"
|
|
||||||
- run: python -m pip install build
|
|
||||||
- name: Build sdist
|
|
||||||
run: python -m build --sdist
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: dist-sdist
|
|
||||||
path: dist/*.tar.gz
|
|
||||||
|
|
||||||
build_wheels:
|
|
||||||
name: Build Google Benchmark wheels on ${{ matrix.os }}
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-latest, ubuntu-24.04-arm, macos-13, macos-14, windows-latest]
|
|
||||||
steps:
|
|
||||||
- name: Check out Google Benchmark
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- uses: actions/setup-python@v5
|
|
||||||
name: Install Python 3.12
|
|
||||||
with:
|
|
||||||
python-version: "3.12"
|
|
||||||
- run: pip install --upgrade pip uv
|
|
||||||
|
|
||||||
- name: Build wheels on ${{ matrix.os }} using cibuildwheel
|
|
||||||
uses: pypa/cibuildwheel@v2.23.2
|
|
||||||
env:
|
|
||||||
CIBW_BUILD: "cp310-* cp311-* cp312-*"
|
|
||||||
CIBW_BUILD_FRONTEND: "build[uv]"
|
|
||||||
CIBW_SKIP: "*-musllinux_*"
|
|
||||||
CIBW_ARCHS: auto64
|
|
||||||
CIBW_BEFORE_ALL_LINUX: bash .github/install_bazel.sh
|
|
||||||
# Grab the rootless Bazel installation inside the container.
|
|
||||||
CIBW_ENVIRONMENT_LINUX: PATH=$PATH:$HOME/bin
|
|
||||||
CIBW_TEST_COMMAND: python {project}/bindings/python/google_benchmark/example.py
|
|
||||||
# unused by Bazel, but needed explicitly by delocate on MacOS.
|
|
||||||
MACOSX_DEPLOYMENT_TARGET: "10.14"
|
|
||||||
|
|
||||||
- name: Upload Google Benchmark ${{ matrix.os }} wheels
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: dist-${{ matrix.os }}
|
|
||||||
path: wheelhouse/*.whl
|
|
||||||
|
|
||||||
pypi_upload:
|
|
||||||
name: Publish google-benchmark wheels to PyPI
|
|
||||||
needs: [build_sdist, build_wheels]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
id-token: write
|
|
||||||
steps:
|
|
||||||
- uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
path: dist
|
|
||||||
pattern: dist-*
|
|
||||||
merge-multiple: true
|
|
||||||
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
||||||
@@ -8,10 +8,8 @@
|
|||||||
!/cmake/*.cmake
|
!/cmake/*.cmake
|
||||||
!/test/AssemblyTests.cmake
|
!/test/AssemblyTests.cmake
|
||||||
*~
|
*~
|
||||||
*.swp
|
|
||||||
*.pyc
|
*.pyc
|
||||||
__pycache__
|
__pycache__
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
# lcov
|
# lcov
|
||||||
*.lcov
|
*.lcov
|
||||||
@@ -46,7 +44,6 @@ rules.ninja
|
|||||||
|
|
||||||
# bazel output symlinks.
|
# bazel output symlinks.
|
||||||
bazel-*
|
bazel-*
|
||||||
MODULE.bazel.lock
|
|
||||||
|
|
||||||
# out-of-source build top-level folders.
|
# out-of-source build top-level folders.
|
||||||
build/
|
build/
|
||||||
@@ -59,10 +56,3 @@ build*/
|
|||||||
# Visual Studio 2015/2017 cache/options directory
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
.vs/
|
.vs/
|
||||||
CMakeSettings.json
|
CMakeSettings.json
|
||||||
|
|
||||||
# Visual Studio Code cache/options directory
|
|
||||||
.vscode/
|
|
||||||
|
|
||||||
# Python build stuff
|
|
||||||
dist/
|
|
||||||
*.egg-info*
|
|
||||||
|
|||||||
-18
@@ -1,18 +0,0 @@
|
|||||||
repos:
|
|
||||||
- repo: https://github.com/keith/pre-commit-buildifier
|
|
||||||
rev: 8.0.3
|
|
||||||
hooks:
|
|
||||||
- id: buildifier
|
|
||||||
- id: buildifier-lint
|
|
||||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
||||||
rev: v1.15.0
|
|
||||||
hooks:
|
|
||||||
- id: mypy
|
|
||||||
types_or: [ python, pyi ]
|
|
||||||
args: [ "--ignore-missing-imports", "--scripts-are-modules" ]
|
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
||||||
rev: v0.11.8
|
|
||||||
hooks:
|
|
||||||
- id: ruff
|
|
||||||
args: [ --fix, --exit-non-zero-on-fix ]
|
|
||||||
- id: ruff-format
|
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Install a newer CMake version
|
||||||
|
curl -sSL https://cmake.org/files/v3.6/cmake-3.6.1-Linux-x86_64.sh -o install-cmake.sh
|
||||||
|
chmod +x install-cmake.sh
|
||||||
|
sudo ./install-cmake.sh --prefix=/usr/local --skip-license
|
||||||
|
|
||||||
|
# Checkout LLVM sources
|
||||||
|
git clone --depth=1 https://github.com/llvm-mirror/llvm.git llvm-source
|
||||||
|
git clone --depth=1 https://github.com/llvm-mirror/libcxx.git llvm-source/projects/libcxx
|
||||||
|
git clone --depth=1 https://github.com/llvm-mirror/libcxxabi.git llvm-source/projects/libcxxabi
|
||||||
|
|
||||||
|
# Setup libc++ options
|
||||||
|
if [ -z "$BUILD_32_BITS" ]; then
|
||||||
|
export BUILD_32_BITS=OFF && echo disabling 32 bit build
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build and install libc++ (Use unstable ABI for better sanitizer coverage)
|
||||||
|
mkdir llvm-build && cd llvm-build
|
||||||
|
cmake -DCMAKE_C_COMPILER=${C_COMPILER} -DCMAKE_CXX_COMPILER=${COMPILER} \
|
||||||
|
-DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=/usr \
|
||||||
|
-DLIBCXX_ABI_UNSTABLE=ON \
|
||||||
|
-DLLVM_USE_SANITIZER=${LIBCXX_SANITIZER} \
|
||||||
|
-DLLVM_BUILD_32_BITS=${BUILD_32_BITS} \
|
||||||
|
../llvm-source
|
||||||
|
make cxx -j2
|
||||||
|
sudo make install-cxxabi install-cxx
|
||||||
|
cd ../
|
||||||
Vendored
+199
@@ -0,0 +1,199 @@
|
|||||||
|
sudo: required
|
||||||
|
dist: trusty
|
||||||
|
language: cpp
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- /usr/local/bin:$PATH
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- compiler: gcc
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- lcov
|
||||||
|
env: COMPILER=g++ C_COMPILER=gcc BUILD_TYPE=Coverage
|
||||||
|
- compiler: gcc
|
||||||
|
env: COMPILER=g++ C_COMPILER=gcc BUILD_TYPE=Debug
|
||||||
|
- compiler: gcc
|
||||||
|
env: COMPILER=g++ C_COMPILER=gcc BUILD_TYPE=Release
|
||||||
|
- compiler: gcc
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- g++-multilib
|
||||||
|
env: COMPILER=g++ C_COMPILER=gcc BUILD_TYPE=Debug BUILD_32_BITS=ON
|
||||||
|
- compiler: gcc
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- g++-multilib
|
||||||
|
env: COMPILER=g++ C_COMPILER=gcc BUILD_TYPE=Release BUILD_32_BITS=ON
|
||||||
|
- compiler: gcc
|
||||||
|
env:
|
||||||
|
- INSTALL_GCC6_FROM_PPA=1
|
||||||
|
- COMPILER=g++-6 C_COMPILER=gcc-6 BUILD_TYPE=Debug
|
||||||
|
- ENABLE_SANITIZER=1
|
||||||
|
- EXTRA_FLAGS="-fno-omit-frame-pointer -g -O2 -fsanitize=undefined,address -fuse-ld=gold"
|
||||||
|
- compiler: clang
|
||||||
|
env: COMPILER=clang++ C_COMPILER=clang BUILD_TYPE=Debug
|
||||||
|
- compiler: clang
|
||||||
|
env: COMPILER=clang++ C_COMPILER=clang BUILD_TYPE=Release
|
||||||
|
# Clang w/ libc++
|
||||||
|
- compiler: clang
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
clang-3.8
|
||||||
|
env:
|
||||||
|
- COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=Debug
|
||||||
|
- LIBCXX_BUILD=1
|
||||||
|
- EXTRA_FLAGS="-stdlib=libc++"
|
||||||
|
- compiler: clang
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
clang-3.8
|
||||||
|
env:
|
||||||
|
- COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=Release
|
||||||
|
- LIBCXX_BUILD=1
|
||||||
|
- EXTRA_FLAGS="-stdlib=libc++"
|
||||||
|
# Clang w/ 32bit libc++
|
||||||
|
- compiler: clang
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- clang-3.8
|
||||||
|
- g++-multilib
|
||||||
|
env:
|
||||||
|
- COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=Debug
|
||||||
|
- LIBCXX_BUILD=1
|
||||||
|
- BUILD_32_BITS=ON
|
||||||
|
- EXTRA_FLAGS="-stdlib=libc++ -m32"
|
||||||
|
# Clang w/ 32bit libc++
|
||||||
|
- compiler: clang
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- clang-3.8
|
||||||
|
- g++-multilib
|
||||||
|
env:
|
||||||
|
- COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=Release
|
||||||
|
- LIBCXX_BUILD=1
|
||||||
|
- BUILD_32_BITS=ON
|
||||||
|
- EXTRA_FLAGS="-stdlib=libc++ -m32"
|
||||||
|
# Clang w/ libc++, ASAN, UBSAN
|
||||||
|
- compiler: clang
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
clang-3.8
|
||||||
|
env:
|
||||||
|
- COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=Debug
|
||||||
|
- LIBCXX_BUILD=1 LIBCXX_SANITIZER="Undefined;Address"
|
||||||
|
- ENABLE_SANITIZER=1
|
||||||
|
- EXTRA_FLAGS="-stdlib=libc++ -g -O2 -fno-omit-frame-pointer -fsanitize=undefined,address -fno-sanitize-recover=all"
|
||||||
|
- UBSAN_OPTIONS=print_stacktrace=1
|
||||||
|
# Clang w/ libc++ and MSAN
|
||||||
|
- compiler: clang
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
clang-3.8
|
||||||
|
env:
|
||||||
|
- COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=Debug
|
||||||
|
- LIBCXX_BUILD=1 LIBCXX_SANITIZER=MemoryWithOrigins
|
||||||
|
- ENABLE_SANITIZER=1
|
||||||
|
- EXTRA_FLAGS="-stdlib=libc++ -g -O2 -fno-omit-frame-pointer -fsanitize=memory -fsanitize-memory-track-origins"
|
||||||
|
# Clang w/ libc++ and MSAN
|
||||||
|
- compiler: clang
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
clang-3.8
|
||||||
|
env:
|
||||||
|
- COMPILER=clang++-3.8 C_COMPILER=clang-3.8 BUILD_TYPE=RelWithDebInfo
|
||||||
|
- LIBCXX_BUILD=1 LIBCXX_SANITIZER=Thread
|
||||||
|
- ENABLE_SANITIZER=1
|
||||||
|
- EXTRA_FLAGS="-stdlib=libc++ -g -O2 -fno-omit-frame-pointer -fsanitize=thread -fno-sanitize-recover=all"
|
||||||
|
- os: osx
|
||||||
|
osx_image: xcode8.3
|
||||||
|
compiler: clang
|
||||||
|
env:
|
||||||
|
- COMPILER=clang++ BUILD_TYPE=Debug
|
||||||
|
- os: osx
|
||||||
|
osx_image: xcode8.3
|
||||||
|
compiler: clang
|
||||||
|
env:
|
||||||
|
- COMPILER=clang++ BUILD_TYPE=Release
|
||||||
|
- os: osx
|
||||||
|
osx_image: xcode8.3
|
||||||
|
compiler: clang
|
||||||
|
env:
|
||||||
|
- COMPILER=clang++ BUILD_TYPE=Release BUILD_32_BITS=ON
|
||||||
|
- os: osx
|
||||||
|
osx_image: xcode8.3
|
||||||
|
compiler: gcc
|
||||||
|
env:
|
||||||
|
- COMPILER=g++-7 C_COMPILER=gcc-7 BUILD_TYPE=Debug
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- if [ -n "${LIBCXX_BUILD}" ]; then
|
||||||
|
source .travis-libcxx-setup.sh;
|
||||||
|
fi
|
||||||
|
- if [ -n "${ENABLE_SANITIZER}" ]; then
|
||||||
|
export EXTRA_OPTIONS="-DBENCHMARK_ENABLE_ASSEMBLY_TESTS=OFF";
|
||||||
|
else
|
||||||
|
export EXTRA_OPTIONS="";
|
||||||
|
fi
|
||||||
|
- mkdir -p build && cd build
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- if [ -z "$BUILD_32_BITS" ]; then
|
||||||
|
export BUILD_32_BITS=OFF && echo disabling 32 bit build;
|
||||||
|
fi
|
||||||
|
- if [ -n "${INSTALL_GCC6_FROM_PPA}" ]; then
|
||||||
|
sudo add-apt-repository -y "ppa:ubuntu-toolchain-r/test";
|
||||||
|
sudo apt-get update --option Acquire::Retries=100 --option Acquire::http::Timeout="60";
|
||||||
|
fi
|
||||||
|
|
||||||
|
install:
|
||||||
|
- if [ -n "${INSTALL_GCC6_FROM_PPA}" ]; then
|
||||||
|
travis_wait sudo -E apt-get -yq --no-install-suggests --no-install-recommends install g++-6;
|
||||||
|
fi
|
||||||
|
- if [ "${TRAVIS_OS_NAME}" == "linux" -a "${BUILD_32_BITS}" == "OFF" ]; then
|
||||||
|
travis_wait sudo -E apt-get -y --no-install-suggests --no-install-recommends install llvm-3.9-tools;
|
||||||
|
sudo cp /usr/lib/llvm-3.9/bin/FileCheck /usr/local/bin/;
|
||||||
|
fi
|
||||||
|
- if [ "${BUILD_TYPE}" == "Coverage" -a "${TRAVIS_OS_NAME}" == "linux" ]; then
|
||||||
|
PATH=~/.local/bin:${PATH};
|
||||||
|
pip install --user --upgrade pip;
|
||||||
|
travis_wait pip install --user cpp-coveralls;
|
||||||
|
fi
|
||||||
|
- if [ "${C_COMPILER}" == "gcc-7" -a "${TRAVIS_OS_NAME}" == "osx" ]; then
|
||||||
|
rm -f /usr/local/include/c++;
|
||||||
|
brew update;
|
||||||
|
travis_wait brew install gcc@7;
|
||||||
|
fi
|
||||||
|
- if [ "${TRAVIS_OS_NAME}" == "linux" ]; then
|
||||||
|
sudo apt-get update -qq;
|
||||||
|
sudo apt-get install -qq unzip;
|
||||||
|
wget https://github.com/bazelbuild/bazel/releases/download/0.10.1/bazel-0.10.1-installer-linux-x86_64.sh --output-document bazel-installer.sh;
|
||||||
|
travis_wait sudo bash bazel-installer.sh;
|
||||||
|
fi
|
||||||
|
- if [ "${TRAVIS_OS_NAME}" == "osx" ]; then
|
||||||
|
curl -L -o bazel-installer.sh https://github.com/bazelbuild/bazel/releases/download/0.10.1/bazel-0.10.1-installer-darwin-x86_64.sh;
|
||||||
|
travis_wait sudo bash bazel-installer.sh;
|
||||||
|
fi
|
||||||
|
|
||||||
|
script:
|
||||||
|
- cmake -DCMAKE_C_COMPILER=${C_COMPILER} -DCMAKE_CXX_COMPILER=${COMPILER} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_CXX_FLAGS="${EXTRA_FLAGS}" -DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON -DBENCHMARK_BUILD_32_BITS=${BUILD_32_BITS} ${EXTRA_OPTIONS} ..
|
||||||
|
- make
|
||||||
|
- ctest -C ${BUILD_TYPE} --output-on-failure
|
||||||
|
- bazel test -c dbg --define google_benchmark.have_regex=posix --announce_rc --verbose_failures --test_output=errors --keep_going //test/...
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- if [ "${BUILD_TYPE}" == "Coverage" -a "${TRAVIS_OS_NAME}" == "linux" ]; then
|
||||||
|
coveralls --include src --include include --gcov-options '\-lp' --root .. --build-root .;
|
||||||
|
fi
|
||||||
+80
-85
@@ -1,30 +1,25 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
import ycm_core
|
import ycm_core
|
||||||
|
|
||||||
# These are the compilation flags that will be used in case there's no
|
# These are the compilation flags that will be used in case there's no
|
||||||
# compilation database set (by default, one is not set).
|
# compilation database set (by default, one is not set).
|
||||||
# CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR.
|
# CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR.
|
||||||
flags = [
|
flags = [
|
||||||
"-Wall",
|
'-Wall',
|
||||||
"-Werror",
|
'-Werror',
|
||||||
"-pedantic-errors",
|
'-pedantic-errors',
|
||||||
"-std=c++0x",
|
'-std=c++0x',
|
||||||
"-fno-strict-aliasing",
|
'-fno-strict-aliasing',
|
||||||
"-O3",
|
'-O3',
|
||||||
"-DNDEBUG",
|
'-DNDEBUG',
|
||||||
# ...and the same thing goes for the magic -x option which specifies the
|
# ...and the same thing goes for the magic -x option which specifies the
|
||||||
# language that the files to be compiled are written in. This is mostly
|
# language that the files to be compiled are written in. This is mostly
|
||||||
# relevant for c++ headers.
|
# relevant for c++ headers.
|
||||||
# For a C project, you would set this to 'c' instead of 'c++'.
|
# For a C project, you would set this to 'c' instead of 'c++'.
|
||||||
"-x",
|
'-x', 'c++',
|
||||||
"c++",
|
'-I', 'include',
|
||||||
"-I",
|
'-isystem', '/usr/include',
|
||||||
"include",
|
'-isystem', '/usr/local/include',
|
||||||
"-isystem",
|
|
||||||
"/usr/include",
|
|
||||||
"-isystem",
|
|
||||||
"/usr/local/include",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -34,87 +29,87 @@ flags = [
|
|||||||
#
|
#
|
||||||
# Most projects will NOT need to set this to anything; you can just change the
|
# Most projects will NOT need to set this to anything; you can just change the
|
||||||
# 'flags' list of compilation flags. Notice that YCM itself uses that approach.
|
# 'flags' list of compilation flags. Notice that YCM itself uses that approach.
|
||||||
compilation_database_folder = ""
|
compilation_database_folder = ''
|
||||||
|
|
||||||
if os.path.exists(compilation_database_folder):
|
if os.path.exists( compilation_database_folder ):
|
||||||
database = ycm_core.CompilationDatabase(compilation_database_folder)
|
database = ycm_core.CompilationDatabase( compilation_database_folder )
|
||||||
else:
|
else:
|
||||||
database = None
|
database = None
|
||||||
|
|
||||||
SOURCE_EXTENSIONS = [".cc"]
|
|
||||||
|
|
||||||
|
SOURCE_EXTENSIONS = [ '.cc' ]
|
||||||
|
|
||||||
def DirectoryOfThisScript():
|
def DirectoryOfThisScript():
|
||||||
return os.path.dirname(os.path.abspath(__file__))
|
return os.path.dirname( os.path.abspath( __file__ ) )
|
||||||
|
|
||||||
|
|
||||||
def MakeRelativePathsInFlagsAbsolute(flags, working_directory):
|
def MakeRelativePathsInFlagsAbsolute( flags, working_directory ):
|
||||||
if not working_directory:
|
if not working_directory:
|
||||||
return list(flags)
|
return list( flags )
|
||||||
new_flags = []
|
new_flags = []
|
||||||
make_next_absolute = False
|
make_next_absolute = False
|
||||||
path_flags = ["-isystem", "-I", "-iquote", "--sysroot="]
|
path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ]
|
||||||
for flag in flags:
|
for flag in flags:
|
||||||
new_flag = flag
|
new_flag = flag
|
||||||
|
|
||||||
if make_next_absolute:
|
if make_next_absolute:
|
||||||
make_next_absolute = False
|
make_next_absolute = False
|
||||||
if not flag.startswith("/"):
|
if not flag.startswith( '/' ):
|
||||||
new_flag = os.path.join(working_directory, flag)
|
new_flag = os.path.join( working_directory, flag )
|
||||||
|
|
||||||
for path_flag in path_flags:
|
for path_flag in path_flags:
|
||||||
if flag == path_flag:
|
if flag == path_flag:
|
||||||
make_next_absolute = True
|
make_next_absolute = True
|
||||||
break
|
break
|
||||||
|
|
||||||
if flag.startswith(path_flag):
|
if flag.startswith( path_flag ):
|
||||||
path = flag[len(path_flag) :]
|
path = flag[ len( path_flag ): ]
|
||||||
new_flag = path_flag + os.path.join(working_directory, path)
|
new_flag = path_flag + os.path.join( working_directory, path )
|
||||||
break
|
break
|
||||||
|
|
||||||
if new_flag:
|
if new_flag:
|
||||||
new_flags.append(new_flag)
|
new_flags.append( new_flag )
|
||||||
return new_flags
|
return new_flags
|
||||||
|
|
||||||
|
|
||||||
def IsHeaderFile(filename):
|
def IsHeaderFile( filename ):
|
||||||
extension = os.path.splitext(filename)[1]
|
extension = os.path.splitext( filename )[ 1 ]
|
||||||
return extension in [".h", ".hxx", ".hpp", ".hh"]
|
return extension in [ '.h', '.hxx', '.hpp', '.hh' ]
|
||||||
|
|
||||||
|
|
||||||
def GetCompilationInfoForFile(filename):
|
def GetCompilationInfoForFile( filename ):
|
||||||
# The compilation_commands.json file generated by CMake does not have
|
# The compilation_commands.json file generated by CMake does not have entries
|
||||||
# entries for header files. So we do our best by asking the db for flags for
|
# for header files. So we do our best by asking the db for flags for a
|
||||||
# a corresponding source file, if any. If one exists, the flags for that
|
# corresponding source file, if any. If one exists, the flags for that file
|
||||||
# file should be good enough.
|
# should be good enough.
|
||||||
if IsHeaderFile(filename):
|
if IsHeaderFile( filename ):
|
||||||
basename = os.path.splitext(filename)[0]
|
basename = os.path.splitext( filename )[ 0 ]
|
||||||
for extension in SOURCE_EXTENSIONS:
|
for extension in SOURCE_EXTENSIONS:
|
||||||
replacement_file = basename + extension
|
replacement_file = basename + extension
|
||||||
if os.path.exists(replacement_file):
|
if os.path.exists( replacement_file ):
|
||||||
compilation_info = database.GetCompilationInfoForFile(
|
compilation_info = database.GetCompilationInfoForFile(
|
||||||
replacement_file
|
replacement_file )
|
||||||
)
|
if compilation_info.compiler_flags_:
|
||||||
if compilation_info.compiler_flags_:
|
return compilation_info
|
||||||
return compilation_info
|
return None
|
||||||
return None
|
return database.GetCompilationInfoForFile( filename )
|
||||||
return database.GetCompilationInfoForFile(filename)
|
|
||||||
|
|
||||||
|
|
||||||
def FlagsForFile(filename, **kwargs):
|
def FlagsForFile( filename, **kwargs ):
|
||||||
if database:
|
if database:
|
||||||
# Bear in mind that compilation_info.compiler_flags_ does NOT return a
|
# Bear in mind that compilation_info.compiler_flags_ does NOT return a
|
||||||
# python list, but a "list-like" StringVec object
|
# python list, but a "list-like" StringVec object
|
||||||
compilation_info = GetCompilationInfoForFile(filename)
|
compilation_info = GetCompilationInfoForFile( filename )
|
||||||
if not compilation_info:
|
if not compilation_info:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
final_flags = MakeRelativePathsInFlagsAbsolute(
|
final_flags = MakeRelativePathsInFlagsAbsolute(
|
||||||
compilation_info.compiler_flags_,
|
compilation_info.compiler_flags_,
|
||||||
compilation_info.compiler_working_dir_,
|
compilation_info.compiler_working_dir_ )
|
||||||
)
|
else:
|
||||||
else:
|
relative_to = DirectoryOfThisScript()
|
||||||
relative_to = DirectoryOfThisScript()
|
final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to )
|
||||||
final_flags = MakeRelativePathsInFlagsAbsolute(flags, relative_to)
|
|
||||||
|
|
||||||
return {"flags": final_flags, "do_cache": True}
|
return {
|
||||||
|
'flags': final_flags,
|
||||||
|
'do_cache': True
|
||||||
|
}
|
||||||
|
|||||||
Vendored
-24
@@ -9,64 +9,40 @@
|
|||||||
# Please keep the list sorted.
|
# Please keep the list sorted.
|
||||||
|
|
||||||
Albert Pretorius <pretoalb@gmail.com>
|
Albert Pretorius <pretoalb@gmail.com>
|
||||||
Alex Steele <steeleal123@gmail.com>
|
|
||||||
Andriy Berestovskyy <berestovskyy@gmail.com>
|
|
||||||
Arne Beer <arne@twobeer.de>
|
Arne Beer <arne@twobeer.de>
|
||||||
Carto
|
Carto
|
||||||
Cezary Skrzyński <czars1988@gmail.com>
|
|
||||||
Christian Wassermann <christian_wassermann@web.de>
|
|
||||||
Christopher Seymour <chris.j.seymour@hotmail.com>
|
Christopher Seymour <chris.j.seymour@hotmail.com>
|
||||||
Colin Braley <braley.colin@gmail.com>
|
|
||||||
Daniel Harvey <danielharvey458@gmail.com>
|
|
||||||
David Coeurjolly <david.coeurjolly@liris.cnrs.fr>
|
David Coeurjolly <david.coeurjolly@liris.cnrs.fr>
|
||||||
Deniz Evrenci <denizevrenci@gmail.com>
|
Deniz Evrenci <denizevrenci@gmail.com>
|
||||||
Dirac Research
|
Dirac Research
|
||||||
Dominik Czarnota <dominik.b.czarnota@gmail.com>
|
Dominik Czarnota <dominik.b.czarnota@gmail.com>
|
||||||
Dominik Korman <kormandominik@gmail.com>
|
|
||||||
Donald Aingworth <donalds_junk_mail@yahoo.com>
|
|
||||||
Eric Backus <eric_backus@alum.mit.edu>
|
|
||||||
Eric Fiselier <eric@efcs.ca>
|
Eric Fiselier <eric@efcs.ca>
|
||||||
Eugene Zhuk <eugene.zhuk@gmail.com>
|
Eugene Zhuk <eugene.zhuk@gmail.com>
|
||||||
Evgeny Safronov <division494@gmail.com>
|
Evgeny Safronov <division494@gmail.com>
|
||||||
Fabien Pichot <pichot.fabien@gmail.com>
|
|
||||||
Federico Ficarelli <federico.ficarelli@gmail.com>
|
Federico Ficarelli <federico.ficarelli@gmail.com>
|
||||||
Felix Homann <linuxaudio@showlabor.de>
|
Felix Homann <linuxaudio@showlabor.de>
|
||||||
Gergely Meszaros <maetveis@gmail.com>
|
|
||||||
Gergő Szitár <szitar.gergo@gmail.com>
|
|
||||||
Google Inc.
|
Google Inc.
|
||||||
Henrique Bucher <hbucher@gmail.com>
|
|
||||||
International Business Machines Corporation
|
International Business Machines Corporation
|
||||||
Ismael Jimenez Martinez <ismael.jimenez.martinez@gmail.com>
|
Ismael Jimenez Martinez <ismael.jimenez.martinez@gmail.com>
|
||||||
Jern-Kuan Leong <jernkuan@gmail.com>
|
Jern-Kuan Leong <jernkuan@gmail.com>
|
||||||
JianXiong Zhou <zhoujianxiong2@gmail.com>
|
JianXiong Zhou <zhoujianxiong2@gmail.com>
|
||||||
Joao Paulo Magalhaes <joaoppmagalhaes@gmail.com>
|
Joao Paulo Magalhaes <joaoppmagalhaes@gmail.com>
|
||||||
Jordan Williams <jwillikers@protonmail.com>
|
|
||||||
Jussi Knuuttila <jussi.knuuttila@gmail.com>
|
Jussi Knuuttila <jussi.knuuttila@gmail.com>
|
||||||
Kaito Udagawa <umireon@gmail.com>
|
Kaito Udagawa <umireon@gmail.com>
|
||||||
Kishan Kumar <kumar.kishan@outlook.com>
|
Kishan Kumar <kumar.kishan@outlook.com>
|
||||||
Lei Xu <eddyxu@gmail.com>
|
Lei Xu <eddyxu@gmail.com>
|
||||||
Marcel Jacobse <mjacobse@uni-bremen.de>
|
|
||||||
Matt Clarkson <mattyclarkson@gmail.com>
|
Matt Clarkson <mattyclarkson@gmail.com>
|
||||||
Maxim Vafin <maxvafin@gmail.com>
|
Maxim Vafin <maxvafin@gmail.com>
|
||||||
Mike Apodaca <gatorfax@gmail.com>
|
|
||||||
Min-Yih Hsu <yihshyng223@gmail.com>
|
|
||||||
MongoDB Inc.
|
MongoDB Inc.
|
||||||
Nick Hutchinson <nshutchinson@gmail.com>
|
Nick Hutchinson <nshutchinson@gmail.com>
|
||||||
Norman Heino <norman.heino@gmail.com>
|
|
||||||
Oleksandr Sochka <sasha.sochka@gmail.com>
|
Oleksandr Sochka <sasha.sochka@gmail.com>
|
||||||
Ori Livneh <ori.livneh@gmail.com>
|
Ori Livneh <ori.livneh@gmail.com>
|
||||||
Paul Redmond <paul.redmond@gmail.com>
|
Paul Redmond <paul.redmond@gmail.com>
|
||||||
Radoslav Yovchev <radoslav.tm@gmail.com>
|
Radoslav Yovchev <radoslav.tm@gmail.com>
|
||||||
Raghu Raja <raghu@enfabrica.net>
|
|
||||||
Rainer Orth <ro@cebitec.uni-bielefeld.de>
|
|
||||||
Roman Lebedev <lebedev.ri@gmail.com>
|
Roman Lebedev <lebedev.ri@gmail.com>
|
||||||
Sayan Bhattacharjee <aero.sayan@gmail.com>
|
|
||||||
Shapr3D <google-contributors@shapr3d.com>
|
|
||||||
Shuo Chen <chenshuo@chenshuo.com>
|
Shuo Chen <chenshuo@chenshuo.com>
|
||||||
Staffan Tjernstrom <staffantj@gmail.com>
|
|
||||||
Steinar H. Gunderson <sgunderson@bigfoot.com>
|
Steinar H. Gunderson <sgunderson@bigfoot.com>
|
||||||
Stripe, Inc.
|
Stripe, Inc.
|
||||||
Tobias Schmidt <tobias.schmidt@in.tum.de>
|
|
||||||
Yixuan Qiu <yixuanq@gmail.com>
|
Yixuan Qiu <yixuanq@gmail.com>
|
||||||
Yusuke Suzuki <utatane.tea@gmail.com>
|
Yusuke Suzuki <utatane.tea@gmail.com>
|
||||||
Zbigniew Skowron <zbychs@gmail.com>
|
Zbigniew Skowron <zbychs@gmail.com>
|
||||||
|
|||||||
Vendored
+6
-66
@@ -1,38 +1,9 @@
|
|||||||
load("@rules_cc//cc:defs.bzl", "cc_library")
|
|
||||||
|
|
||||||
licenses(["notice"])
|
licenses(["notice"])
|
||||||
|
|
||||||
COPTS = [
|
|
||||||
"-pedantic",
|
|
||||||
"-pedantic-errors",
|
|
||||||
"-std=c++17",
|
|
||||||
"-Wall",
|
|
||||||
"-Wconversion",
|
|
||||||
"-Wextra",
|
|
||||||
"-Wshadow",
|
|
||||||
# "-Wshorten-64-to-32",
|
|
||||||
"-Wfloat-equal",
|
|
||||||
"-fstrict-aliasing",
|
|
||||||
## assert() are used a lot in tests upstream, which may be optimised out leading to
|
|
||||||
## unused-variable warning.
|
|
||||||
"-Wno-unused-variable",
|
|
||||||
"-Werror=old-style-cast",
|
|
||||||
]
|
|
||||||
|
|
||||||
MSVC_COPTS = [
|
|
||||||
"/std:c++17",
|
|
||||||
]
|
|
||||||
|
|
||||||
config_setting(
|
config_setting(
|
||||||
name = "windows",
|
name = "windows",
|
||||||
constraint_values = ["@platforms//os:windows"],
|
values = {
|
||||||
visibility = [":__subpackages__"],
|
"cpu": "x64_windows",
|
||||||
)
|
|
||||||
|
|
||||||
config_setting(
|
|
||||||
name = "perfcounters",
|
|
||||||
define_values = {
|
|
||||||
"pfm": "1",
|
|
||||||
},
|
},
|
||||||
visibility = [":__subpackages__"],
|
visibility = [":__subpackages__"],
|
||||||
)
|
)
|
||||||
@@ -46,51 +17,20 @@ cc_library(
|
|||||||
],
|
],
|
||||||
exclude = ["src/benchmark_main.cc"],
|
exclude = ["src/benchmark_main.cc"],
|
||||||
),
|
),
|
||||||
hdrs = [
|
hdrs = ["include/benchmark/benchmark.h"],
|
||||||
"include/benchmark/benchmark.h",
|
|
||||||
"include/benchmark/export.h",
|
|
||||||
],
|
|
||||||
copts = select({
|
|
||||||
":windows": MSVC_COPTS,
|
|
||||||
"//conditions:default": COPTS,
|
|
||||||
}),
|
|
||||||
defines = [
|
|
||||||
"BENCHMARK_STATIC_DEFINE",
|
|
||||||
"BENCHMARK_VERSION=\\\"" + (module_version() if module_version() != None else "") + "\\\"",
|
|
||||||
] + select({
|
|
||||||
":perfcounters": ["HAVE_LIBPFM"],
|
|
||||||
"//conditions:default": [],
|
|
||||||
}),
|
|
||||||
includes = ["include"],
|
|
||||||
linkopts = select({
|
linkopts = select({
|
||||||
":windows": ["-DEFAULTLIB:shlwapi.lib"],
|
":windows": ["-DEFAULTLIB:shlwapi.lib"],
|
||||||
"//conditions:default": ["-pthread"],
|
"//conditions:default": ["-pthread"],
|
||||||
}),
|
}),
|
||||||
# Only static linking is allowed; no .so will be produced.
|
strip_include_prefix = "include",
|
||||||
# Using `defines` (i.e. not `local_defines`) means that no
|
|
||||||
# dependent rules need to bother about defining the macro.
|
|
||||||
linkstatic = True,
|
|
||||||
local_defines = [
|
|
||||||
# Turn on Large-file Support
|
|
||||||
"_FILE_OFFSET_BITS=64",
|
|
||||||
"_LARGEFILE64_SOURCE",
|
|
||||||
"_LARGEFILE_SOURCE",
|
|
||||||
],
|
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = select({
|
|
||||||
":perfcounters": ["@libpfm"],
|
|
||||||
"//conditions:default": [],
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "benchmark_main",
|
name = "benchmark_main",
|
||||||
srcs = ["src/benchmark_main.cc"],
|
srcs = ["src/benchmark_main.cc"],
|
||||||
hdrs = [
|
hdrs = ["include/benchmark/benchmark.h"],
|
||||||
"include/benchmark/benchmark.h",
|
strip_include_prefix = "include",
|
||||||
"include/benchmark/export.h",
|
|
||||||
],
|
|
||||||
includes = ["include"],
|
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [":benchmark"],
|
deps = [":benchmark"],
|
||||||
)
|
)
|
||||||
|
|||||||
Vendored
+61
-146
@@ -1,34 +1,27 @@
|
|||||||
# Require CMake 3.10. If available, use the policies up to CMake 3.22.
|
cmake_minimum_required (VERSION 2.8.12)
|
||||||
cmake_minimum_required (VERSION 3.13...3.22)
|
|
||||||
|
|
||||||
project (benchmark VERSION 1.9.4 LANGUAGES CXX)
|
project (benchmark)
|
||||||
|
|
||||||
|
foreach(p
|
||||||
|
CMP0054 # CMake 3.1
|
||||||
|
CMP0056 # export EXE_LINKER_FLAGS to try_run
|
||||||
|
CMP0057 # Support no if() IN_LIST operator
|
||||||
|
)
|
||||||
|
if(POLICY ${p})
|
||||||
|
cmake_policy(SET ${p} NEW)
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
option(BENCHMARK_ENABLE_TESTING "Enable testing of the benchmark library." ON)
|
option(BENCHMARK_ENABLE_TESTING "Enable testing of the benchmark library." ON)
|
||||||
option(BENCHMARK_ENABLE_EXCEPTIONS "Enable the use of exceptions in the benchmark library." ON)
|
option(BENCHMARK_ENABLE_EXCEPTIONS "Enable the use of exceptions in the benchmark library." ON)
|
||||||
option(BENCHMARK_ENABLE_LTO "Enable link time optimisation of the benchmark library." OFF)
|
option(BENCHMARK_ENABLE_LTO "Enable link time optimisation of the benchmark library." OFF)
|
||||||
option(BENCHMARK_USE_LIBCXX "Build and test using libc++ as the standard library." OFF)
|
option(BENCHMARK_USE_LIBCXX "Build and test using libc++ as the standard library." OFF)
|
||||||
option(BENCHMARK_ENABLE_WERROR "Build Release candidates with -Werror." ON)
|
if(NOT MSVC)
|
||||||
option(BENCHMARK_FORCE_WERROR "Build Release candidates with -Werror regardless of compiler issues." OFF)
|
|
||||||
|
|
||||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "PGI")
|
|
||||||
# PGC++ maybe reporting false positives.
|
|
||||||
set(BENCHMARK_ENABLE_WERROR OFF)
|
|
||||||
endif()
|
|
||||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "NVHPC")
|
|
||||||
set(BENCHMARK_ENABLE_WERROR OFF)
|
|
||||||
endif()
|
|
||||||
if(BENCHMARK_FORCE_WERROR)
|
|
||||||
set(BENCHMARK_ENABLE_WERROR ON)
|
|
||||||
endif(BENCHMARK_FORCE_WERROR)
|
|
||||||
|
|
||||||
if(NOT (MSVC OR CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC"))
|
|
||||||
option(BENCHMARK_BUILD_32_BITS "Build a 32 bit version of the library." OFF)
|
option(BENCHMARK_BUILD_32_BITS "Build a 32 bit version of the library." OFF)
|
||||||
else()
|
else()
|
||||||
set(BENCHMARK_BUILD_32_BITS OFF CACHE BOOL "Build a 32 bit version of the library - unsupported when using MSVC)" FORCE)
|
set(BENCHMARK_BUILD_32_BITS OFF CACHE BOOL "Build a 32 bit version of the library - unsupported when using MSVC)" FORCE)
|
||||||
endif()
|
endif()
|
||||||
option(BENCHMARK_ENABLE_INSTALL "Enable installation of benchmark. (Projects embedding benchmark may want to turn this OFF.)" ON)
|
option(BENCHMARK_ENABLE_INSTALL "Enable installation of benchmark. (Projects embedding benchmark may want to turn this OFF.)" ON)
|
||||||
option(BENCHMARK_ENABLE_DOXYGEN "Build documentation with Doxygen." OFF)
|
|
||||||
option(BENCHMARK_INSTALL_DOCS "Enable installation of documentation." ON)
|
|
||||||
|
|
||||||
# Allow unmet dependencies to be met using CMake's ExternalProject mechanics, which
|
# Allow unmet dependencies to be met using CMake's ExternalProject mechanics, which
|
||||||
# may require downloading the source code.
|
# may require downloading the source code.
|
||||||
@@ -37,24 +30,6 @@ option(BENCHMARK_DOWNLOAD_DEPENDENCIES "Allow the downloading and in-tree buildi
|
|||||||
# This option can be used to disable building and running unit tests which depend on gtest
|
# This option can be used to disable building and running unit tests which depend on gtest
|
||||||
# in cases where it is not possible to build or find a valid version of gtest.
|
# in cases where it is not possible to build or find a valid version of gtest.
|
||||||
option(BENCHMARK_ENABLE_GTEST_TESTS "Enable building the unit tests which depend on gtest" ON)
|
option(BENCHMARK_ENABLE_GTEST_TESTS "Enable building the unit tests which depend on gtest" ON)
|
||||||
option(BENCHMARK_USE_BUNDLED_GTEST "Use bundled GoogleTest. If disabled, the find_package(GTest) will be used." ON)
|
|
||||||
|
|
||||||
option(BENCHMARK_ENABLE_LIBPFM "Enable performance counters provided by libpfm" OFF)
|
|
||||||
|
|
||||||
# Export only public symbols
|
|
||||||
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
|
|
||||||
set(CMAKE_VISIBILITY_INLINES_HIDDEN ON)
|
|
||||||
|
|
||||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
|
||||||
# As of CMake 3.18, CMAKE_SYSTEM_PROCESSOR is not set properly for MSVC and
|
|
||||||
# cross-compilation (e.g. Host=x86_64, target=aarch64) requires using the
|
|
||||||
# undocumented, but working variable.
|
|
||||||
# See https://gitlab.kitware.com/cmake/cmake/-/issues/15170
|
|
||||||
set(CMAKE_SYSTEM_PROCESSOR ${MSVC_CXX_ARCHITECTURE_ID})
|
|
||||||
if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "ARM")
|
|
||||||
set(CMAKE_CROSSCOMPILING TRUE)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(ENABLE_ASSEMBLY_TESTS_DEFAULT OFF)
|
set(ENABLE_ASSEMBLY_TESTS_DEFAULT OFF)
|
||||||
function(should_enable_assembly_tests)
|
function(should_enable_assembly_tests)
|
||||||
@@ -66,7 +41,7 @@ function(should_enable_assembly_tests)
|
|||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
if (MSVC OR CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC")
|
if (MSVC)
|
||||||
return()
|
return()
|
||||||
elseif(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
|
elseif(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
|
||||||
return()
|
return()
|
||||||
@@ -102,63 +77,29 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
|||||||
include(GetGitVersion)
|
include(GetGitVersion)
|
||||||
get_git_version(GIT_VERSION)
|
get_git_version(GIT_VERSION)
|
||||||
|
|
||||||
# If no git version can be determined, use the version
|
|
||||||
# from the project() command
|
|
||||||
if ("${GIT_VERSION}" STREQUAL "v0.0.0")
|
|
||||||
set(VERSION "v${benchmark_VERSION}")
|
|
||||||
else()
|
|
||||||
set(VERSION "${GIT_VERSION}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Normalize version: drop "v" prefix, replace first "-" with ".",
|
|
||||||
# drop everything after second "-" (including said "-").
|
|
||||||
string(STRIP ${VERSION} VERSION)
|
|
||||||
if(VERSION MATCHES v[^-]*-)
|
|
||||||
string(REGEX REPLACE "v([^-]*)-([0-9]+)-.*" "\\1.\\2" NORMALIZED_VERSION ${VERSION})
|
|
||||||
else()
|
|
||||||
string(REGEX REPLACE "v(.*)" "\\1" NORMALIZED_VERSION ${VERSION})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Tell the user what versions we are using
|
# Tell the user what versions we are using
|
||||||
message(STATUS "Google Benchmark version: ${VERSION}, normalized to ${NORMALIZED_VERSION}")
|
string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" VERSION ${GIT_VERSION})
|
||||||
|
message(STATUS "Version: ${VERSION}")
|
||||||
|
|
||||||
# The version of the libraries
|
# The version of the libraries
|
||||||
set(GENERIC_LIB_VERSION ${NORMALIZED_VERSION})
|
set(GENERIC_LIB_VERSION ${VERSION})
|
||||||
string(SUBSTRING ${NORMALIZED_VERSION} 0 1 GENERIC_LIB_SOVERSION)
|
string(SUBSTRING ${VERSION} 0 1 GENERIC_LIB_SOVERSION)
|
||||||
|
|
||||||
# Import our CMake modules
|
# Import our CMake modules
|
||||||
include(AddCXXCompilerFlag)
|
|
||||||
include(CheckCXXCompilerFlag)
|
include(CheckCXXCompilerFlag)
|
||||||
include(CheckLibraryExists)
|
include(AddCXXCompilerFlag)
|
||||||
include(CXXFeatureCheck)
|
include(CXXFeatureCheck)
|
||||||
|
|
||||||
check_library_exists(rt shm_open "" HAVE_LIB_RT)
|
|
||||||
|
|
||||||
if (BENCHMARK_BUILD_32_BITS)
|
if (BENCHMARK_BUILD_32_BITS)
|
||||||
add_required_cxx_compiler_flag(-m32)
|
add_required_cxx_compiler_flag(-m32)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(BENCHMARK_CXX_STANDARD 17)
|
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD ${BENCHMARK_CXX_STANDARD})
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
|
||||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
# Turn compiler warnings up to 11
|
# Turn compiler warnings up to 11
|
||||||
string(REGEX REPLACE "[-/]W[1-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
string(REGEX REPLACE "[-/]W[1-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
|
||||||
|
|
||||||
# MP flag only applies to cl, not cl frontends to other compilers (e.g. clang-cl, icx-cl etc)
|
|
||||||
if(CMAKE_CXX_COMPILER_ID MATCHES MSVC)
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
|
|
||||||
endif()
|
|
||||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
||||||
|
|
||||||
if(BENCHMARK_ENABLE_WERROR)
|
|
||||||
add_cxx_compiler_flag(-WX)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (NOT BENCHMARK_ENABLE_EXCEPTIONS)
|
if (NOT BENCHMARK_ENABLE_EXCEPTIONS)
|
||||||
add_cxx_compiler_flag(-EHs-)
|
add_cxx_compiler_flag(-EHs-)
|
||||||
add_cxx_compiler_flag(-EHa-)
|
add_cxx_compiler_flag(-EHa-)
|
||||||
@@ -185,48 +126,45 @@ if (MSVC)
|
|||||||
set(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL} /LTCG")
|
set(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL} /LTCG")
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
# Turn on Large-file Support
|
# Try and enable C++11. Don't use C++14 because it doesn't work in some
|
||||||
add_definitions(-D_FILE_OFFSET_BITS=64)
|
# configurations.
|
||||||
add_definitions(-D_LARGEFILE64_SOURCE)
|
add_cxx_compiler_flag(-std=c++11)
|
||||||
add_definitions(-D_LARGEFILE_SOURCE)
|
if (NOT HAVE_CXX_FLAG_STD_CXX11)
|
||||||
|
add_cxx_compiler_flag(-std=c++0x)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Turn compiler warnings up to 11
|
# Turn compiler warnings up to 11
|
||||||
add_cxx_compiler_flag(-Wall)
|
if (NOT MSVC)
|
||||||
add_cxx_compiler_flag(-Wextra)
|
add_cxx_compiler_flag(-Wall)
|
||||||
add_cxx_compiler_flag(-Wshadow)
|
add_cxx_compiler_flag(-Wextra)
|
||||||
add_cxx_compiler_flag(-Wfloat-equal)
|
add_cxx_compiler_flag(-Wshadow)
|
||||||
add_cxx_compiler_flag(-Wold-style-cast)
|
add_cxx_compiler_flag(-Werror RELEASE)
|
||||||
add_cxx_compiler_flag(-Wconversion)
|
add_cxx_compiler_flag(-Werror RELWITHDEBINFO)
|
||||||
if(BENCHMARK_ENABLE_WERROR)
|
add_cxx_compiler_flag(-Werror MINSIZEREL)
|
||||||
add_cxx_compiler_flag(-Werror)
|
add_cxx_compiler_flag(-pedantic)
|
||||||
endif()
|
add_cxx_compiler_flag(-pedantic-errors)
|
||||||
if (NOT BENCHMARK_ENABLE_TESTING)
|
add_cxx_compiler_flag(-Wshorten-64-to-32)
|
||||||
# Disable warning when compiling tests as gtest does not use 'override'.
|
add_cxx_compiler_flag(-fstrict-aliasing)
|
||||||
add_cxx_compiler_flag(-Wsuggest-override)
|
# Disable warnings regarding deprecated parts of the library while building
|
||||||
endif()
|
# and testing those parts of the library.
|
||||||
add_cxx_compiler_flag(-pedantic)
|
add_cxx_compiler_flag(-Wno-deprecated-declarations)
|
||||||
add_cxx_compiler_flag(-pedantic-errors)
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
|
||||||
add_cxx_compiler_flag(-Wshorten-64-to-32)
|
# Intel silently ignores '-Wno-deprecated-declarations',
|
||||||
add_cxx_compiler_flag(-fstrict-aliasing)
|
# warning no. 1786 must be explicitly disabled.
|
||||||
# Disable warnings regarding deprecated parts of the library while building
|
# See #631 for rationale.
|
||||||
# and testing those parts of the library.
|
add_cxx_compiler_flag(-wd1786)
|
||||||
add_cxx_compiler_flag(-Wno-deprecated-declarations)
|
endif()
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Intel" OR CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM")
|
# Disable deprecation warnings for release builds (when -Werror is enabled).
|
||||||
# Intel silently ignores '-Wno-deprecated-declarations',
|
add_cxx_compiler_flag(-Wno-deprecated RELEASE)
|
||||||
# warning no. 1786 must be explicitly disabled.
|
add_cxx_compiler_flag(-Wno-deprecated RELWITHDEBINFO)
|
||||||
# See #631 for rationale.
|
add_cxx_compiler_flag(-Wno-deprecated MINSIZEREL)
|
||||||
add_cxx_compiler_flag(-wd1786)
|
if (NOT BENCHMARK_ENABLE_EXCEPTIONS)
|
||||||
add_cxx_compiler_flag(-fno-finite-math-only)
|
add_cxx_compiler_flag(-fno-exceptions)
|
||||||
endif()
|
endif()
|
||||||
# Disable deprecation warnings for release builds (when -Werror is enabled).
|
|
||||||
if(BENCHMARK_ENABLE_WERROR)
|
|
||||||
add_cxx_compiler_flag(-Wno-deprecated)
|
|
||||||
endif()
|
|
||||||
if (NOT BENCHMARK_ENABLE_EXCEPTIONS)
|
|
||||||
add_cxx_compiler_flag(-fno-exceptions)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (HAVE_CXX_FLAG_FSTRICT_ALIASING)
|
if (HAVE_CXX_FLAG_FSTRICT_ALIASING)
|
||||||
if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Intel" AND NOT CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM") #ICC17u2: Many false positives for Wstrict-aliasing
|
if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Intel") #ICC17u2: Many false positives for Wstrict-aliasing
|
||||||
add_cxx_compiler_flag(-Wstrict-aliasing)
|
add_cxx_compiler_flag(-Wstrict-aliasing)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
@@ -235,26 +173,21 @@ else()
|
|||||||
add_cxx_compiler_flag(-wd654)
|
add_cxx_compiler_flag(-wd654)
|
||||||
add_cxx_compiler_flag(-Wthread-safety)
|
add_cxx_compiler_flag(-Wthread-safety)
|
||||||
if (HAVE_CXX_FLAG_WTHREAD_SAFETY)
|
if (HAVE_CXX_FLAG_WTHREAD_SAFETY)
|
||||||
cxx_feature_check(THREAD_SAFETY_ATTRIBUTES "-DINCLUDE_DIRECTORIES=${PROJECT_SOURCE_DIR}/include")
|
cxx_feature_check(THREAD_SAFETY_ATTRIBUTES)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# On most UNIX like platforms g++ and clang++ define _GNU_SOURCE as a
|
# On most UNIX like platforms g++ and clang++ define _GNU_SOURCE as a
|
||||||
# predefined macro, which turns on all of the wonderful libc extensions.
|
# predefined macro, which turns on all of the wonderful libc extensions.
|
||||||
# However g++ doesn't do this in Cygwin so we have to define it ourselves
|
# However g++ doesn't do this in Cygwin so we have to define it ourselfs
|
||||||
# since we depend on GNU/POSIX/BSD extensions.
|
# since we depend on GNU/POSIX/BSD extensions.
|
||||||
if (CYGWIN)
|
if (CYGWIN)
|
||||||
add_definitions(-D_GNU_SOURCE=1)
|
add_definitions(-D_GNU_SOURCE=1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (QNXNTO)
|
|
||||||
add_definitions(-D_QNX_SOURCE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Link time optimisation
|
# Link time optimisation
|
||||||
if (BENCHMARK_ENABLE_LTO)
|
if (BENCHMARK_ENABLE_LTO)
|
||||||
add_cxx_compiler_flag(-flto)
|
add_cxx_compiler_flag(-flto)
|
||||||
add_cxx_compiler_flag(-Wno-lto-type-mismatch)
|
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
|
||||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
|
||||||
find_program(GCC_AR gcc-ar)
|
find_program(GCC_AR gcc-ar)
|
||||||
if (GCC_AR)
|
if (GCC_AR)
|
||||||
set(CMAKE_AR ${GCC_AR})
|
set(CMAKE_AR ${GCC_AR})
|
||||||
@@ -263,7 +196,7 @@ else()
|
|||||||
if (GCC_RANLIB)
|
if (GCC_RANLIB)
|
||||||
set(CMAKE_RANLIB ${GCC_RANLIB})
|
set(CMAKE_RANLIB ${GCC_RANLIB})
|
||||||
endif()
|
endif()
|
||||||
elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
elseif("${CMAKE_C_COMPILER_ID}" MATCHES "Clang")
|
||||||
include(llvm-toolchain)
|
include(llvm-toolchain)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
@@ -291,8 +224,7 @@ if (BENCHMARK_USE_LIBCXX)
|
|||||||
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||||
add_cxx_compiler_flag(-stdlib=libc++)
|
add_cxx_compiler_flag(-stdlib=libc++)
|
||||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR
|
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR
|
||||||
"${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel" OR
|
"${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")
|
||||||
"${CMAKE_CXX_COMPILER_ID}" STREQUAL "IntelLLVM")
|
|
||||||
add_cxx_compiler_flag(-nostdinc++)
|
add_cxx_compiler_flag(-nostdinc++)
|
||||||
message(WARNING "libc++ header path must be manually specified using CMAKE_CXX_FLAGS")
|
message(WARNING "libc++ header path must be manually specified using CMAKE_CXX_FLAGS")
|
||||||
# Adding -nodefaultlibs directly to CMAKE_<TYPE>_LINKER_FLAGS will break
|
# Adding -nodefaultlibs directly to CMAKE_<TYPE>_LINKER_FLAGS will break
|
||||||
@@ -318,16 +250,9 @@ if (NOT BENCHMARK_ENABLE_EXCEPTIONS AND HAVE_STD_REGEX
|
|||||||
AND NOT HAVE_GNU_POSIX_REGEX AND NOT HAVE_POSIX_REGEX)
|
AND NOT HAVE_GNU_POSIX_REGEX AND NOT HAVE_POSIX_REGEX)
|
||||||
message(WARNING "Using std::regex with exceptions disabled is not fully supported")
|
message(WARNING "Using std::regex with exceptions disabled is not fully supported")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
cxx_feature_check(STEADY_CLOCK)
|
cxx_feature_check(STEADY_CLOCK)
|
||||||
# Ensure we have pthreads
|
# Ensure we have pthreads
|
||||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
cxx_feature_check(PTHREAD_AFFINITY)
|
|
||||||
|
|
||||||
if (BENCHMARK_ENABLE_LIBPFM)
|
|
||||||
find_package(PFM REQUIRED)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Set up directories
|
# Set up directories
|
||||||
include_directories(${PROJECT_SOURCE_DIR}/include)
|
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||||
@@ -337,18 +262,8 @@ add_subdirectory(src)
|
|||||||
|
|
||||||
if (BENCHMARK_ENABLE_TESTING)
|
if (BENCHMARK_ENABLE_TESTING)
|
||||||
enable_testing()
|
enable_testing()
|
||||||
if (BENCHMARK_ENABLE_GTEST_TESTS AND
|
if (BENCHMARK_ENABLE_GTEST_TESTS)
|
||||||
NOT (TARGET gtest AND TARGET gtest_main AND
|
include(HandleGTest)
|
||||||
TARGET gmock AND TARGET gmock_main))
|
|
||||||
if (BENCHMARK_USE_BUNDLED_GTEST)
|
|
||||||
include(GoogleTest)
|
|
||||||
else()
|
|
||||||
find_package(GTest CONFIG REQUIRED)
|
|
||||||
add_library(gtest ALIAS GTest::gtest)
|
|
||||||
add_library(gtest_main ALIAS GTest::gtest_main)
|
|
||||||
add_library(gmock ALIAS GTest::gmock)
|
|
||||||
add_library(gmock_main ALIAS GTest::gmock_main)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
add_subdirectory(test)
|
add_subdirectory(test)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
Vendored
+1
-32
@@ -22,75 +22,44 @@
|
|||||||
#
|
#
|
||||||
# Please keep the list sorted.
|
# Please keep the list sorted.
|
||||||
|
|
||||||
Abhina Sreeskantharajan <abhina.sreeskantharajan@ibm.com>
|
|
||||||
Albert Pretorius <pretoalb@gmail.com>
|
Albert Pretorius <pretoalb@gmail.com>
|
||||||
Alex Steele <steelal123@gmail.com>
|
|
||||||
Andriy Berestovskyy <berestovskyy@gmail.com>
|
|
||||||
Arne Beer <arne@twobeer.de>
|
Arne Beer <arne@twobeer.de>
|
||||||
Bátor Tallér <bator.taller@shapr3d.com>
|
|
||||||
Billy Robert O'Neal III <billy.oneal@gmail.com> <bion@microsoft.com>
|
Billy Robert O'Neal III <billy.oneal@gmail.com> <bion@microsoft.com>
|
||||||
Cezary Skrzyński <czars1988@gmail.com>
|
|
||||||
Chris Kennelly <ckennelly@google.com> <ckennelly@ckennelly.com>
|
Chris Kennelly <ckennelly@google.com> <ckennelly@ckennelly.com>
|
||||||
Christian Wassermann <christian_wassermann@web.de>
|
|
||||||
Christopher Seymour <chris.j.seymour@hotmail.com>
|
Christopher Seymour <chris.j.seymour@hotmail.com>
|
||||||
Colin Braley <braley.colin@gmail.com>
|
|
||||||
Cyrille Faucheux <cyrille.faucheux@gmail.com>
|
|
||||||
Daniel Harvey <danielharvey458@gmail.com>
|
|
||||||
David Coeurjolly <david.coeurjolly@liris.cnrs.fr>
|
David Coeurjolly <david.coeurjolly@liris.cnrs.fr>
|
||||||
Deniz Evrenci <denizevrenci@gmail.com>
|
Deniz Evrenci <denizevrenci@gmail.com>
|
||||||
Dominic Hamon <dma@stripysock.com> <dominic@google.com>
|
Dominic Hamon <dma@stripysock.com> <dominic@google.com>
|
||||||
Dominik Czarnota <dominik.b.czarnota@gmail.com>
|
Dominik Czarnota <dominik.b.czarnota@gmail.com>
|
||||||
Dominik Korman <kormandominik@gmail.com>
|
|
||||||
Donald Aingworth <donalds_junk_mail@yahoo.com>
|
|
||||||
Doug Evans <xdje42@gmail.com>
|
|
||||||
Eric Backus <eric_backus@alum.mit.edu>
|
|
||||||
Eric Fiselier <eric@efcs.ca>
|
Eric Fiselier <eric@efcs.ca>
|
||||||
Eugene Zhuk <eugene.zhuk@gmail.com>
|
Eugene Zhuk <eugene.zhuk@gmail.com>
|
||||||
Evgeny Safronov <division494@gmail.com>
|
Evgeny Safronov <division494@gmail.com>
|
||||||
Fabien Pichot <pichot.fabien@gmail.com>
|
|
||||||
Fanbo Meng <fanbo.meng@ibm.com>
|
|
||||||
Federico Ficarelli <federico.ficarelli@gmail.com>
|
Federico Ficarelli <federico.ficarelli@gmail.com>
|
||||||
Felix Homann <linuxaudio@showlabor.de>
|
Felix Homann <linuxaudio@showlabor.de>
|
||||||
Geoffrey Martin-Noble <gcmn@google.com> <gmngeoffrey@gmail.com>
|
|
||||||
Gergely Meszaros <maetveis@gmail.com>
|
|
||||||
Gergő Szitár <szitar.gergo@gmail.com>
|
|
||||||
Hannes Hauswedell <h2@fsfe.org>
|
|
||||||
Henrique Bucher <hbucher@gmail.com>
|
|
||||||
Ismael Jimenez Martinez <ismael.jimenez.martinez@gmail.com>
|
Ismael Jimenez Martinez <ismael.jimenez.martinez@gmail.com>
|
||||||
Iakov Sergeev <yahontu@gmail.com>
|
|
||||||
Jern-Kuan Leong <jernkuan@gmail.com>
|
Jern-Kuan Leong <jernkuan@gmail.com>
|
||||||
JianXiong Zhou <zhoujianxiong2@gmail.com>
|
JianXiong Zhou <zhoujianxiong2@gmail.com>
|
||||||
Joao Paulo Magalhaes <joaoppmagalhaes@gmail.com>
|
Joao Paulo Magalhaes <joaoppmagalhaes@gmail.com>
|
||||||
John Millikin <jmillikin@stripe.com>
|
John Millikin <jmillikin@stripe.com>
|
||||||
Jordan Williams <jwillikers@protonmail.com>
|
|
||||||
Jussi Knuuttila <jussi.knuuttila@gmail.com>
|
Jussi Knuuttila <jussi.knuuttila@gmail.com>
|
||||||
Kaito Udagawa <umireon@gmail.com>
|
|
||||||
Kai Wolf <kai.wolf@gmail.com>
|
Kai Wolf <kai.wolf@gmail.com>
|
||||||
Kishan Kumar <kumar.kishan@outlook.com>
|
Kishan Kumar <kumar.kishan@outlook.com>
|
||||||
|
Kaito Udagawa <umireon@gmail.com>
|
||||||
Lei Xu <eddyxu@gmail.com>
|
Lei Xu <eddyxu@gmail.com>
|
||||||
Marcel Jacobse <mjacobse@uni-bremen.de>
|
|
||||||
Matt Clarkson <mattyclarkson@gmail.com>
|
Matt Clarkson <mattyclarkson@gmail.com>
|
||||||
Maxim Vafin <maxvafin@gmail.com>
|
Maxim Vafin <maxvafin@gmail.com>
|
||||||
Mike Apodaca <gatorfax@gmail.com>
|
|
||||||
Min-Yih Hsu <yihshyng223@gmail.com>
|
|
||||||
Nick Hutchinson <nshutchinson@gmail.com>
|
Nick Hutchinson <nshutchinson@gmail.com>
|
||||||
Norman Heino <norman.heino@gmail.com>
|
|
||||||
Oleksandr Sochka <sasha.sochka@gmail.com>
|
Oleksandr Sochka <sasha.sochka@gmail.com>
|
||||||
Ori Livneh <ori.livneh@gmail.com>
|
Ori Livneh <ori.livneh@gmail.com>
|
||||||
Pascal Leroy <phl@google.com>
|
Pascal Leroy <phl@google.com>
|
||||||
Paul Redmond <paul.redmond@gmail.com>
|
Paul Redmond <paul.redmond@gmail.com>
|
||||||
Pierre Phaneuf <pphaneuf@google.com>
|
Pierre Phaneuf <pphaneuf@google.com>
|
||||||
Radoslav Yovchev <radoslav.tm@gmail.com>
|
Radoslav Yovchev <radoslav.tm@gmail.com>
|
||||||
Raghu Raja <raghu@enfabrica.net>
|
|
||||||
Rainer Orth <ro@cebitec.uni-bielefeld.de>
|
|
||||||
Raul Marin <rmrodriguez@cartodb.com>
|
Raul Marin <rmrodriguez@cartodb.com>
|
||||||
Ray Glover <ray.glover@uk.ibm.com>
|
Ray Glover <ray.glover@uk.ibm.com>
|
||||||
Robert Guo <robert.guo@mongodb.com>
|
Robert Guo <robert.guo@mongodb.com>
|
||||||
Roman Lebedev <lebedev.ri@gmail.com>
|
Roman Lebedev <lebedev.ri@gmail.com>
|
||||||
Sayan Bhattacharjee <aero.sayan@gmail.com>
|
|
||||||
Shuo Chen <chenshuo@chenshuo.com>
|
Shuo Chen <chenshuo@chenshuo.com>
|
||||||
Steven Wan <wan.yu@ibm.com>
|
|
||||||
Tobias Schmidt <tobias.schmidt@in.tum.de>
|
|
||||||
Tobias Ulvgård <tobias.ulvgard@dirac.se>
|
Tobias Ulvgård <tobias.ulvgard@dirac.se>
|
||||||
Tom Madams <tom.ej.madams@gmail.com> <tmadams@google.com>
|
Tom Madams <tom.ej.madams@gmail.com> <tmadams@google.com>
|
||||||
Yixuan Qiu <yixuanq@gmail.com>
|
Yixuan Qiu <yixuanq@gmail.com>
|
||||||
|
|||||||
Vendored
-41
@@ -1,41 +0,0 @@
|
|||||||
module(
|
|
||||||
name = "google_benchmark",
|
|
||||||
version = "1.9.4",
|
|
||||||
)
|
|
||||||
|
|
||||||
bazel_dep(name = "bazel_skylib", version = "1.7.1")
|
|
||||||
bazel_dep(name = "platforms", version = "0.0.10")
|
|
||||||
bazel_dep(name = "rules_cc", version = "0.0.9")
|
|
||||||
|
|
||||||
bazel_dep(name = "rules_python", version = "1.0.0", dev_dependency = True)
|
|
||||||
bazel_dep(name = "googletest", version = "1.14.0", dev_dependency = True, repo_name = "com_google_googletest")
|
|
||||||
|
|
||||||
bazel_dep(name = "libpfm", version = "4.11.0.bcr.1")
|
|
||||||
|
|
||||||
# Register a toolchain for Python 3.9 to be able to build numpy. Python
|
|
||||||
# versions >=3.10 are problematic.
|
|
||||||
# A second reason for this is to be able to build Python hermetically instead
|
|
||||||
# of relying on the changing default version from rules_python.
|
|
||||||
|
|
||||||
python = use_extension("@rules_python//python/extensions:python.bzl", "python", dev_dependency = True)
|
|
||||||
python.toolchain(python_version = "3.8")
|
|
||||||
python.toolchain(python_version = "3.9")
|
|
||||||
python.toolchain(python_version = "3.10")
|
|
||||||
python.toolchain(python_version = "3.11")
|
|
||||||
python.toolchain(
|
|
||||||
is_default = True,
|
|
||||||
python_version = "3.12",
|
|
||||||
)
|
|
||||||
python.toolchain(python_version = "3.13")
|
|
||||||
|
|
||||||
pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip", dev_dependency = True)
|
|
||||||
pip.parse(
|
|
||||||
hub_name = "tools_pip_deps",
|
|
||||||
python_version = "3.9",
|
|
||||||
requirements_lock = "//tools:requirements.txt",
|
|
||||||
)
|
|
||||||
use_repo(pip, "tools_pip_deps")
|
|
||||||
|
|
||||||
# -- bazel_dep definitions -- #
|
|
||||||
|
|
||||||
bazel_dep(name = "nanobind_bazel", version = "2.7.0", dev_dependency = True)
|
|
||||||
Vendored
+903
-144
File diff suppressed because it is too large
Load Diff
Vendored
+4
-17
@@ -1,20 +1,7 @@
|
|||||||
workspace(name = "com_github_google_benchmark")
|
workspace(name = "com_github_google_benchmark")
|
||||||
|
|
||||||
load("//:bazel/benchmark_deps.bzl", "benchmark_deps")
|
http_archive(
|
||||||
|
name = "com_google_googletest",
|
||||||
benchmark_deps()
|
urls = ["https://github.com/google/googletest/archive/3f0cf6b62ad1eb50d8736538363d3580dd640c3e.zip"],
|
||||||
|
strip_prefix = "googletest-3f0cf6b62ad1eb50d8736538363d3580dd640c3e",
|
||||||
load("@rules_python//python:repositories.bzl", "py_repositories")
|
|
||||||
|
|
||||||
py_repositories()
|
|
||||||
|
|
||||||
load("@rules_python//python:pip.bzl", "pip_parse")
|
|
||||||
|
|
||||||
pip_parse(
|
|
||||||
name = "tools_pip_deps",
|
|
||||||
requirements_lock = "//tools:requirements.txt",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
load("@tools_pip_deps//:requirements.bzl", "install_deps")
|
|
||||||
|
|
||||||
install_deps()
|
|
||||||
|
|||||||
-2
@@ -1,2 +0,0 @@
|
|||||||
# This file marks the root of the Bazel workspace.
|
|
||||||
# See MODULE.bazel for dependencies and setup.
|
|
||||||
Vendored
-2
@@ -1,2 +0,0 @@
|
|||||||
theme: jekyll-theme-midnight
|
|
||||||
markdown: GFM
|
|
||||||
Vendored
+1
-1
@@ -41,7 +41,7 @@ build_script:
|
|||||||
- cmake --build . --config %configuration%
|
- cmake --build . --config %configuration%
|
||||||
|
|
||||||
test_script:
|
test_script:
|
||||||
- ctest --build-config %configuration% --timeout 300 --output-on-failure
|
- ctest -c %configuration% --timeout 300 --output-on-failure
|
||||||
|
|
||||||
artifacts:
|
artifacts:
|
||||||
- path: '_build/CMakeFiles/*.log'
|
- path: '_build/CMakeFiles/*.log'
|
||||||
|
|||||||
-54
@@ -1,54 +0,0 @@
|
|||||||
"""
|
|
||||||
This file contains the Bazel build dependencies for Google Benchmark (both C++ source and Python bindings).
|
|
||||||
"""
|
|
||||||
|
|
||||||
load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository")
|
|
||||||
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
|
||||||
|
|
||||||
def benchmark_deps():
|
|
||||||
"""Loads dependencies required to build Google Benchmark."""
|
|
||||||
|
|
||||||
if "bazel_skylib" not in native.existing_rules():
|
|
||||||
http_archive(
|
|
||||||
name = "bazel_skylib",
|
|
||||||
sha256 = "cd55a062e763b9349921f0f5db8c3933288dc8ba4f76dd9416aac68acee3cb94",
|
|
||||||
urls = [
|
|
||||||
"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz",
|
|
||||||
"https://github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
if "rules_python" not in native.existing_rules():
|
|
||||||
http_archive(
|
|
||||||
name = "rules_python",
|
|
||||||
sha256 = "e85ae30de33625a63eca7fc40a94fea845e641888e52f32b6beea91e8b1b2793",
|
|
||||||
strip_prefix = "rules_python-0.27.1",
|
|
||||||
url = "https://github.com/bazelbuild/rules_python/releases/download/0.27.1/rules_python-0.27.1.tar.gz",
|
|
||||||
)
|
|
||||||
|
|
||||||
if "com_google_googletest" not in native.existing_rules():
|
|
||||||
new_git_repository(
|
|
||||||
name = "com_google_googletest",
|
|
||||||
remote = "https://github.com/google/googletest.git",
|
|
||||||
tag = "release-1.12.1",
|
|
||||||
)
|
|
||||||
|
|
||||||
if "nanobind" not in native.existing_rules():
|
|
||||||
new_git_repository(
|
|
||||||
name = "nanobind",
|
|
||||||
remote = "https://github.com/wjakob/nanobind.git",
|
|
||||||
tag = "v1.9.2",
|
|
||||||
build_file = "@//bindings/python:nanobind.BUILD",
|
|
||||||
recursive_init_submodules = True,
|
|
||||||
)
|
|
||||||
|
|
||||||
if "libpfm" not in native.existing_rules():
|
|
||||||
# Downloaded from v4.9.0 tag at https://sourceforge.net/p/perfmon2/libpfm4/ref/master/tags/
|
|
||||||
http_archive(
|
|
||||||
name = "libpfm",
|
|
||||||
build_file = str(Label("//tools:libpfm.BUILD.bazel")),
|
|
||||||
sha256 = "5da5f8872bde14b3634c9688d980f68bda28b510268723cc12973eedbab9fecc",
|
|
||||||
type = "tar.gz",
|
|
||||||
strip_prefix = "libpfm-4.11.0",
|
|
||||||
urls = ["https://sourceforge.net/projects/perfmon2/files/libpfm4/libpfm-4.11.0.tar.gz/download"],
|
|
||||||
)
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
load("@nanobind_bazel//:build_defs.bzl", "nanobind_extension", "nanobind_stubgen")
|
|
||||||
load("@rules_python//python:defs.bzl", "py_library", "py_test")
|
|
||||||
|
|
||||||
py_library(
|
|
||||||
name = "google_benchmark",
|
|
||||||
srcs = ["__init__.py"],
|
|
||||||
visibility = ["//visibility:public"],
|
|
||||||
deps = [
|
|
||||||
":_benchmark",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
nanobind_extension(
|
|
||||||
name = "_benchmark",
|
|
||||||
srcs = ["benchmark.cc"],
|
|
||||||
deps = ["//:benchmark"],
|
|
||||||
)
|
|
||||||
|
|
||||||
nanobind_stubgen(
|
|
||||||
name = "benchmark_stubgen",
|
|
||||||
marker_file = "bindings/python/google_benchmark/py.typed",
|
|
||||||
module = ":_benchmark",
|
|
||||||
)
|
|
||||||
|
|
||||||
py_test(
|
|
||||||
name = "example",
|
|
||||||
srcs = ["example.py"],
|
|
||||||
python_version = "PY3",
|
|
||||||
srcs_version = "PY3",
|
|
||||||
visibility = ["//visibility:public"],
|
|
||||||
deps = [
|
|
||||||
":google_benchmark",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
# Copyright 2020 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.
|
|
||||||
"""Python benchmarking utilities.
|
|
||||||
|
|
||||||
Example usage:
|
|
||||||
import google_benchmark as benchmark
|
|
||||||
|
|
||||||
@benchmark.register
|
|
||||||
def my_benchmark(state):
|
|
||||||
... # Code executed outside `while` loop is not timed.
|
|
||||||
|
|
||||||
while state:
|
|
||||||
... # Code executed within `while` loop is timed.
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
benchmark.main()
|
|
||||||
"""
|
|
||||||
|
|
||||||
import atexit
|
|
||||||
|
|
||||||
from absl import app
|
|
||||||
|
|
||||||
from google_benchmark import _benchmark
|
|
||||||
from google_benchmark._benchmark import (
|
|
||||||
Counter as Counter,
|
|
||||||
State as State,
|
|
||||||
kMicrosecond as kMicrosecond,
|
|
||||||
kMillisecond as kMillisecond,
|
|
||||||
kNanosecond as kNanosecond,
|
|
||||||
kSecond as kSecond,
|
|
||||||
o1 as o1,
|
|
||||||
oAuto as oAuto,
|
|
||||||
oLambda as oLambda,
|
|
||||||
oLogN as oLogN,
|
|
||||||
oN as oN,
|
|
||||||
oNCubed as oNCubed,
|
|
||||||
oNLogN as oNLogN,
|
|
||||||
oNone as oNone,
|
|
||||||
oNSquared as oNSquared,
|
|
||||||
)
|
|
||||||
|
|
||||||
__version__ = "1.9.4"
|
|
||||||
|
|
||||||
|
|
||||||
class __OptionMaker:
|
|
||||||
"""A stateless class to collect benchmark options.
|
|
||||||
|
|
||||||
Collect all decorator calls like @option.range(start=0, limit=1<<5).
|
|
||||||
"""
|
|
||||||
|
|
||||||
class Options:
|
|
||||||
"""Pure data class to store options calls, along with the benchmarked
|
|
||||||
function."""
|
|
||||||
|
|
||||||
def __init__(self, func):
|
|
||||||
self.func = func
|
|
||||||
self.builder_calls = []
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def make(cls, func_or_options):
|
|
||||||
"""Make Options from Options or the benchmarked function."""
|
|
||||||
if isinstance(func_or_options, cls.Options):
|
|
||||||
return func_or_options
|
|
||||||
return cls.Options(func_or_options)
|
|
||||||
|
|
||||||
def __getattr__(self, builder_name):
|
|
||||||
"""Append option call in the Options."""
|
|
||||||
|
|
||||||
# The function that get returned on @option.range(start=0, limit=1<<5).
|
|
||||||
def __builder_method(*args, **kwargs):
|
|
||||||
# The decorator that get called, either with the benchmared function
|
|
||||||
# or the previous Options
|
|
||||||
def __decorator(func_or_options):
|
|
||||||
options = self.make(func_or_options)
|
|
||||||
options.builder_calls.append((builder_name, args, kwargs))
|
|
||||||
# The decorator returns Options so it is not technically a
|
|
||||||
# decorator and needs a final call to @register
|
|
||||||
return options
|
|
||||||
|
|
||||||
return __decorator
|
|
||||||
|
|
||||||
return __builder_method
|
|
||||||
|
|
||||||
|
|
||||||
# Alias for nicer API.
|
|
||||||
# We have to instantiate an object, even if stateless, to be able to use
|
|
||||||
# __getattr__ on option.range
|
|
||||||
option = __OptionMaker()
|
|
||||||
|
|
||||||
|
|
||||||
def register(undefined=None, *, name=None):
|
|
||||||
"""Register function for benchmarking."""
|
|
||||||
if undefined is None:
|
|
||||||
# Decorator is called without parenthesis so we return a decorator
|
|
||||||
return lambda f: register(f, name=name)
|
|
||||||
|
|
||||||
# We have either the function to benchmark (simple case) or an instance of
|
|
||||||
# Options (@option._ case).
|
|
||||||
options = __OptionMaker.make(undefined)
|
|
||||||
|
|
||||||
if name is None:
|
|
||||||
name = options.func.__name__
|
|
||||||
|
|
||||||
# We register the benchmark and reproduce all the @option._ calls onto the
|
|
||||||
# benchmark builder pattern
|
|
||||||
benchmark = _benchmark.RegisterBenchmark(name, options.func)
|
|
||||||
for name, args, kwargs in options.builder_calls[::-1]:
|
|
||||||
getattr(benchmark, name)(*args, **kwargs)
|
|
||||||
|
|
||||||
# return the benchmarked function because the decorator does not modify it
|
|
||||||
return options.func
|
|
||||||
|
|
||||||
|
|
||||||
def _flags_parser(argv):
|
|
||||||
argv = _benchmark.Initialize(argv)
|
|
||||||
return app.parse_flags_with_usage(argv)
|
|
||||||
|
|
||||||
|
|
||||||
def _run_benchmarks(argv):
|
|
||||||
if len(argv) > 1:
|
|
||||||
raise app.UsageError("Too many command-line arguments.")
|
|
||||||
return _benchmark.RunSpecifiedBenchmarks()
|
|
||||||
|
|
||||||
|
|
||||||
def main(argv=None):
|
|
||||||
return app.run(_run_benchmarks, argv=argv, flags_parser=_flags_parser)
|
|
||||||
|
|
||||||
|
|
||||||
# FIXME: can we rerun with disabled ASLR?
|
|
||||||
|
|
||||||
# Methods for use with custom main function.
|
|
||||||
initialize = _benchmark.Initialize
|
|
||||||
run_benchmarks = _benchmark.RunSpecifiedBenchmarks
|
|
||||||
atexit.register(_benchmark.ClearRegisteredBenchmarks)
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
// Benchmark for Python.
|
|
||||||
|
|
||||||
#include "benchmark/benchmark.h"
|
|
||||||
|
|
||||||
#include "nanobind/nanobind.h"
|
|
||||||
#include "nanobind/operators.h"
|
|
||||||
#include "nanobind/stl/bind_map.h"
|
|
||||||
#include "nanobind/stl/string.h"
|
|
||||||
#include "nanobind/stl/vector.h"
|
|
||||||
|
|
||||||
NB_MAKE_OPAQUE(benchmark::UserCounters);
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
namespace nb = nanobind;
|
|
||||||
|
|
||||||
std::vector<std::string> Initialize(const std::vector<std::string>& argv) {
|
|
||||||
// The `argv` pointers here become invalid when this function returns, but
|
|
||||||
// benchmark holds the pointer to `argv[0]`. We create a static copy of it
|
|
||||||
// so it persists, and replace the pointer below.
|
|
||||||
static std::string executable_name(argv[0]);
|
|
||||||
std::vector<char*> ptrs;
|
|
||||||
ptrs.reserve(argv.size());
|
|
||||||
for (auto& arg : argv) {
|
|
||||||
ptrs.push_back(const_cast<char*>(arg.c_str()));
|
|
||||||
}
|
|
||||||
ptrs[0] = const_cast<char*>(executable_name.c_str());
|
|
||||||
int argc = static_cast<int>(argv.size());
|
|
||||||
benchmark::Initialize(&argc, ptrs.data());
|
|
||||||
std::vector<std::string> remaining_argv;
|
|
||||||
remaining_argv.reserve(argc);
|
|
||||||
for (int i = 0; i < argc; ++i) {
|
|
||||||
remaining_argv.emplace_back(ptrs[i]);
|
|
||||||
}
|
|
||||||
return remaining_argv;
|
|
||||||
}
|
|
||||||
|
|
||||||
benchmark::internal::Benchmark* RegisterBenchmark(const std::string& name,
|
|
||||||
nb::callable f) {
|
|
||||||
return benchmark::RegisterBenchmark(
|
|
||||||
name, [f](benchmark::State& state) { f(&state); });
|
|
||||||
}
|
|
||||||
|
|
||||||
NB_MODULE(_benchmark, m) {
|
|
||||||
|
|
||||||
using benchmark::TimeUnit;
|
|
||||||
nb::enum_<TimeUnit>(m, "TimeUnit")
|
|
||||||
.value("kNanosecond", TimeUnit::kNanosecond)
|
|
||||||
.value("kMicrosecond", TimeUnit::kMicrosecond)
|
|
||||||
.value("kMillisecond", TimeUnit::kMillisecond)
|
|
||||||
.value("kSecond", TimeUnit::kSecond)
|
|
||||||
.export_values();
|
|
||||||
|
|
||||||
using benchmark::BigO;
|
|
||||||
nb::enum_<BigO>(m, "BigO")
|
|
||||||
.value("oNone", BigO::oNone)
|
|
||||||
.value("o1", BigO::o1)
|
|
||||||
.value("oN", BigO::oN)
|
|
||||||
.value("oNSquared", BigO::oNSquared)
|
|
||||||
.value("oNCubed", BigO::oNCubed)
|
|
||||||
.value("oLogN", BigO::oLogN)
|
|
||||||
.value("oNLogN", BigO::oNLogN)
|
|
||||||
.value("oAuto", BigO::oAuto)
|
|
||||||
.value("oLambda", BigO::oLambda)
|
|
||||||
.export_values();
|
|
||||||
|
|
||||||
using benchmark::internal::Benchmark;
|
|
||||||
nb::class_<Benchmark>(m, "Benchmark")
|
|
||||||
// For methods returning a pointer to the current object, reference
|
|
||||||
// return policy is used to ask nanobind not to take ownership of the
|
|
||||||
// returned object and avoid calling delete on it.
|
|
||||||
// https://pybind11.readthedocs.io/en/stable/advanced/functions.html#return-value-policies
|
|
||||||
//
|
|
||||||
// For methods taking a const std::vector<...>&, a copy is created
|
|
||||||
// because a it is bound to a Python list.
|
|
||||||
// https://pybind11.readthedocs.io/en/stable/advanced/cast/stl.html
|
|
||||||
.def("unit", &Benchmark::Unit, nb::rv_policy::reference)
|
|
||||||
.def("arg", &Benchmark::Arg, nb::rv_policy::reference)
|
|
||||||
.def("args", &Benchmark::Args, nb::rv_policy::reference)
|
|
||||||
.def("range", &Benchmark::Range, nb::rv_policy::reference,
|
|
||||||
nb::arg("start"), nb::arg("limit"))
|
|
||||||
.def("dense_range", &Benchmark::DenseRange,
|
|
||||||
nb::rv_policy::reference, nb::arg("start"),
|
|
||||||
nb::arg("limit"), nb::arg("step") = 1)
|
|
||||||
.def("ranges", &Benchmark::Ranges, nb::rv_policy::reference)
|
|
||||||
.def("args_product", &Benchmark::ArgsProduct,
|
|
||||||
nb::rv_policy::reference)
|
|
||||||
.def("arg_name", &Benchmark::ArgName, nb::rv_policy::reference)
|
|
||||||
.def("arg_names", &Benchmark::ArgNames,
|
|
||||||
nb::rv_policy::reference)
|
|
||||||
.def("range_pair", &Benchmark::RangePair,
|
|
||||||
nb::rv_policy::reference, nb::arg("lo1"), nb::arg("hi1"),
|
|
||||||
nb::arg("lo2"), nb::arg("hi2"))
|
|
||||||
.def("range_multiplier", &Benchmark::RangeMultiplier,
|
|
||||||
nb::rv_policy::reference)
|
|
||||||
.def("min_time", &Benchmark::MinTime, nb::rv_policy::reference)
|
|
||||||
.def("min_warmup_time", &Benchmark::MinWarmUpTime,
|
|
||||||
nb::rv_policy::reference)
|
|
||||||
.def("iterations", &Benchmark::Iterations,
|
|
||||||
nb::rv_policy::reference)
|
|
||||||
.def("repetitions", &Benchmark::Repetitions,
|
|
||||||
nb::rv_policy::reference)
|
|
||||||
.def("report_aggregates_only", &Benchmark::ReportAggregatesOnly,
|
|
||||||
nb::rv_policy::reference, nb::arg("value") = true)
|
|
||||||
.def("display_aggregates_only", &Benchmark::DisplayAggregatesOnly,
|
|
||||||
nb::rv_policy::reference, nb::arg("value") = true)
|
|
||||||
.def("measure_process_cpu_time", &Benchmark::MeasureProcessCPUTime,
|
|
||||||
nb::rv_policy::reference)
|
|
||||||
.def("use_real_time", &Benchmark::UseRealTime,
|
|
||||||
nb::rv_policy::reference)
|
|
||||||
.def("use_manual_time", &Benchmark::UseManualTime,
|
|
||||||
nb::rv_policy::reference)
|
|
||||||
.def(
|
|
||||||
"complexity",
|
|
||||||
(Benchmark * (Benchmark::*)(benchmark::BigO)) & Benchmark::Complexity,
|
|
||||||
nb::rv_policy::reference,
|
|
||||||
nb::arg("complexity") = benchmark::oAuto);
|
|
||||||
|
|
||||||
using benchmark::Counter;
|
|
||||||
nb::class_<Counter> py_counter(m, "Counter");
|
|
||||||
|
|
||||||
nb::enum_<Counter::Flags>(py_counter, "Flags", nb::is_arithmetic(), nb::is_flag())
|
|
||||||
.value("kDefaults", Counter::Flags::kDefaults)
|
|
||||||
.value("kIsRate", Counter::Flags::kIsRate)
|
|
||||||
.value("kAvgThreads", Counter::Flags::kAvgThreads)
|
|
||||||
.value("kAvgThreadsRate", Counter::Flags::kAvgThreadsRate)
|
|
||||||
.value("kIsIterationInvariant", Counter::Flags::kIsIterationInvariant)
|
|
||||||
.value("kIsIterationInvariantRate",
|
|
||||||
Counter::Flags::kIsIterationInvariantRate)
|
|
||||||
.value("kAvgIterations", Counter::Flags::kAvgIterations)
|
|
||||||
.value("kAvgIterationsRate", Counter::Flags::kAvgIterationsRate)
|
|
||||||
.value("kInvert", Counter::Flags::kInvert)
|
|
||||||
.export_values();
|
|
||||||
|
|
||||||
nb::enum_<Counter::OneK>(py_counter, "OneK")
|
|
||||||
.value("kIs1000", Counter::OneK::kIs1000)
|
|
||||||
.value("kIs1024", Counter::OneK::kIs1024)
|
|
||||||
.export_values();
|
|
||||||
|
|
||||||
py_counter
|
|
||||||
.def(nb::init<double, Counter::Flags, Counter::OneK>(),
|
|
||||||
nb::arg("value") = 0., nb::arg("flags") = Counter::kDefaults,
|
|
||||||
nb::arg("k") = Counter::kIs1000)
|
|
||||||
.def("__init__",
|
|
||||||
([](Counter* c, double value) { new (c) Counter(value); }))
|
|
||||||
.def_rw("value", &Counter::value)
|
|
||||||
.def_rw("flags", &Counter::flags)
|
|
||||||
.def_rw("oneK", &Counter::oneK)
|
|
||||||
.def(nb::init_implicit<double>());
|
|
||||||
|
|
||||||
nb::implicitly_convertible<nb::int_, Counter>();
|
|
||||||
|
|
||||||
nb::bind_map<benchmark::UserCounters>(m, "UserCounters");
|
|
||||||
|
|
||||||
using benchmark::State;
|
|
||||||
nb::class_<State>(m, "State")
|
|
||||||
.def("__bool__", &State::KeepRunning)
|
|
||||||
.def_prop_ro("keep_running", &State::KeepRunning)
|
|
||||||
.def("pause_timing", &State::PauseTiming)
|
|
||||||
.def("resume_timing", &State::ResumeTiming)
|
|
||||||
.def("skip_with_error", &State::SkipWithError)
|
|
||||||
.def_prop_ro("error_occurred", &State::error_occurred)
|
|
||||||
.def("set_iteration_time", &State::SetIterationTime)
|
|
||||||
.def_prop_rw("bytes_processed", &State::bytes_processed,
|
|
||||||
&State::SetBytesProcessed)
|
|
||||||
.def_prop_rw("complexity_n", &State::complexity_length_n,
|
|
||||||
&State::SetComplexityN)
|
|
||||||
.def_prop_rw("items_processed", &State::items_processed,
|
|
||||||
&State::SetItemsProcessed)
|
|
||||||
.def("set_label", &State::SetLabel)
|
|
||||||
.def("range", &State::range, nb::arg("pos") = 0)
|
|
||||||
.def_prop_ro("iterations", &State::iterations)
|
|
||||||
.def_prop_ro("name", &State::name)
|
|
||||||
.def_rw("counters", &State::counters)
|
|
||||||
.def_prop_ro("thread_index", &State::thread_index)
|
|
||||||
.def_prop_ro("threads", &State::threads);
|
|
||||||
|
|
||||||
m.def("Initialize", Initialize);
|
|
||||||
m.def("RegisterBenchmark", RegisterBenchmark,
|
|
||||||
nb::rv_policy::reference);
|
|
||||||
m.def("RunSpecifiedBenchmarks",
|
|
||||||
[]() { benchmark::RunSpecifiedBenchmarks(); });
|
|
||||||
m.def("ClearRegisteredBenchmarks", benchmark::ClearRegisteredBenchmarks);
|
|
||||||
};
|
|
||||||
} // namespace
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
# Copyright 2020 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.
|
|
||||||
"""Example of Python using C++ benchmark framework.
|
|
||||||
|
|
||||||
To run this example, you must first install the `google_benchmark` Python
|
|
||||||
package.
|
|
||||||
|
|
||||||
To install using `setup.py`, download and extract the `google_benchmark` source.
|
|
||||||
In the extracted directory, execute:
|
|
||||||
python setup.py install
|
|
||||||
"""
|
|
||||||
|
|
||||||
import random
|
|
||||||
import time
|
|
||||||
|
|
||||||
import google_benchmark as benchmark
|
|
||||||
from google_benchmark import Counter
|
|
||||||
|
|
||||||
|
|
||||||
@benchmark.register
|
|
||||||
def empty(state):
|
|
||||||
while state:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@benchmark.register
|
|
||||||
def sum_million(state):
|
|
||||||
while state:
|
|
||||||
sum(range(1_000_000))
|
|
||||||
|
|
||||||
|
|
||||||
@benchmark.register
|
|
||||||
def pause_timing(state):
|
|
||||||
"""Pause timing every iteration."""
|
|
||||||
while state:
|
|
||||||
# Construct a list of random ints every iteration without timing it
|
|
||||||
state.pause_timing()
|
|
||||||
random_list = [random.randint(0, 100) for _ in range(100)]
|
|
||||||
state.resume_timing()
|
|
||||||
# Time the in place sorting algorithm
|
|
||||||
random_list.sort()
|
|
||||||
|
|
||||||
|
|
||||||
@benchmark.register
|
|
||||||
def skipped(state):
|
|
||||||
if True: # Test some predicate here.
|
|
||||||
state.skip_with_error("some error")
|
|
||||||
return # NOTE: You must explicitly return, or benchmark will continue.
|
|
||||||
|
|
||||||
# Benchmark code would be here.
|
|
||||||
|
|
||||||
|
|
||||||
@benchmark.register
|
|
||||||
@benchmark.option.use_manual_time()
|
|
||||||
def manual_timing(state):
|
|
||||||
while state:
|
|
||||||
# Manually count Python CPU time
|
|
||||||
start = time.perf_counter() # perf_counter_ns() in Python 3.7+
|
|
||||||
# Something to benchmark
|
|
||||||
time.sleep(0.01)
|
|
||||||
end = time.perf_counter()
|
|
||||||
state.set_iteration_time(end - start)
|
|
||||||
|
|
||||||
|
|
||||||
@benchmark.register
|
|
||||||
def custom_counters(state):
|
|
||||||
"""Collect custom metric using benchmark.Counter."""
|
|
||||||
num_foo = 0.0
|
|
||||||
while state:
|
|
||||||
# Benchmark some code here
|
|
||||||
# Collect some custom metric named foo
|
|
||||||
num_foo += 0.13
|
|
||||||
|
|
||||||
# Automatic Counter from numbers.
|
|
||||||
state.counters["foo"] = num_foo
|
|
||||||
# Set a counter as a rate.
|
|
||||||
state.counters["foo_rate"] = Counter(num_foo, Counter.kIsRate)
|
|
||||||
# Set a counter as an inverse of rate.
|
|
||||||
state.counters["foo_inv_rate"] = Counter(
|
|
||||||
num_foo, Counter.kIsRate | Counter.kInvert
|
|
||||||
)
|
|
||||||
# Set a counter as a thread-average quantity.
|
|
||||||
state.counters["foo_avg"] = Counter(num_foo, Counter.kAvgThreads)
|
|
||||||
# There's also a combined flag:
|
|
||||||
state.counters["foo_avg_rate"] = Counter(num_foo, Counter.kAvgThreadsRate)
|
|
||||||
|
|
||||||
|
|
||||||
@benchmark.register
|
|
||||||
@benchmark.option.measure_process_cpu_time()
|
|
||||||
@benchmark.option.use_real_time()
|
|
||||||
def with_options(state):
|
|
||||||
while state:
|
|
||||||
sum(range(1_000_000))
|
|
||||||
|
|
||||||
|
|
||||||
@benchmark.register(name="sum_million_microseconds")
|
|
||||||
@benchmark.option.unit(benchmark.kMicrosecond)
|
|
||||||
def with_options2(state):
|
|
||||||
while state:
|
|
||||||
sum(range(1_000_000))
|
|
||||||
|
|
||||||
|
|
||||||
@benchmark.register
|
|
||||||
@benchmark.option.arg(100)
|
|
||||||
@benchmark.option.arg(1000)
|
|
||||||
def passing_argument(state):
|
|
||||||
while state:
|
|
||||||
sum(range(state.range(0)))
|
|
||||||
|
|
||||||
|
|
||||||
@benchmark.register
|
|
||||||
@benchmark.option.range(8, limit=8 << 10)
|
|
||||||
def using_range(state):
|
|
||||||
while state:
|
|
||||||
sum(range(state.range(0)))
|
|
||||||
|
|
||||||
|
|
||||||
@benchmark.register
|
|
||||||
@benchmark.option.range_multiplier(2)
|
|
||||||
@benchmark.option.range(1 << 10, 1 << 18)
|
|
||||||
@benchmark.option.complexity(benchmark.oN)
|
|
||||||
def computing_complexity(state):
|
|
||||||
while state:
|
|
||||||
sum(range(state.range(0)))
|
|
||||||
state.complexity_n = state.range(0)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
benchmark.main()
|
|
||||||
+4
-8
@@ -34,11 +34,9 @@ function(add_cxx_compiler_flag FLAG)
|
|||||||
check_cxx_compiler_flag("${FLAG}" ${MANGLED_FLAG})
|
check_cxx_compiler_flag("${FLAG}" ${MANGLED_FLAG})
|
||||||
set(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}")
|
set(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}")
|
||||||
if(${MANGLED_FLAG})
|
if(${MANGLED_FLAG})
|
||||||
if(ARGC GREATER 1)
|
set(VARIANT ${ARGV1})
|
||||||
set(VARIANT ${ARGV1})
|
if(ARGV1)
|
||||||
string(TOUPPER "_${VARIANT}" VARIANT)
|
string(TOUPPER "_${VARIANT}" VARIANT)
|
||||||
else()
|
|
||||||
set(VARIANT "")
|
|
||||||
endif()
|
endif()
|
||||||
set(CMAKE_CXX_FLAGS${VARIANT} "${CMAKE_CXX_FLAGS${VARIANT}} ${BENCHMARK_CXX_FLAGS${VARIANT}} ${FLAG}" PARENT_SCOPE)
|
set(CMAKE_CXX_FLAGS${VARIANT} "${CMAKE_CXX_FLAGS${VARIANT}} ${BENCHMARK_CXX_FLAGS${VARIANT}} ${FLAG}" PARENT_SCOPE)
|
||||||
endif()
|
endif()
|
||||||
@@ -51,11 +49,9 @@ function(add_required_cxx_compiler_flag FLAG)
|
|||||||
check_cxx_compiler_flag("${FLAG}" ${MANGLED_FLAG})
|
check_cxx_compiler_flag("${FLAG}" ${MANGLED_FLAG})
|
||||||
set(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}")
|
set(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}")
|
||||||
if(${MANGLED_FLAG})
|
if(${MANGLED_FLAG})
|
||||||
if(ARGC GREATER 1)
|
set(VARIANT ${ARGV1})
|
||||||
set(VARIANT ${ARGV1})
|
if(ARGV1)
|
||||||
string(TOUPPER "_${VARIANT}" VARIANT)
|
string(TOUPPER "_${VARIANT}" VARIANT)
|
||||||
else()
|
|
||||||
set(VARIANT "")
|
|
||||||
endif()
|
endif()
|
||||||
set(CMAKE_CXX_FLAGS${VARIANT} "${CMAKE_CXX_FLAGS${VARIANT}} ${FLAG}" PARENT_SCOPE)
|
set(CMAKE_CXX_FLAGS${VARIANT} "${CMAKE_CXX_FLAGS${VARIANT}} ${FLAG}" PARENT_SCOPE)
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FLAG}" PARENT_SCOPE)
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FLAG}" PARENT_SCOPE)
|
||||||
|
|||||||
+9
-27
@@ -17,8 +17,6 @@ if(__cxx_feature_check)
|
|||||||
endif()
|
endif()
|
||||||
set(__cxx_feature_check INCLUDED)
|
set(__cxx_feature_check INCLUDED)
|
||||||
|
|
||||||
option(CXXFEATURECHECK_DEBUG OFF)
|
|
||||||
|
|
||||||
function(cxx_feature_check FILE)
|
function(cxx_feature_check FILE)
|
||||||
string(TOLOWER ${FILE} FILE)
|
string(TOLOWER ${FILE} FILE)
|
||||||
string(TOUPPER ${FILE} VAR)
|
string(TOUPPER ${FILE} VAR)
|
||||||
@@ -29,38 +27,26 @@ function(cxx_feature_check FILE)
|
|||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(FEATURE_CHECK_CMAKE_FLAGS ${BENCHMARK_CXX_LINKER_FLAGS})
|
|
||||||
if (ARGC GREATER 1)
|
|
||||||
message(STATUS "Enabling additional flags: ${ARGV1}")
|
|
||||||
list(APPEND FEATURE_CHECK_CMAKE_FLAGS ${ARGV1})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (NOT DEFINED COMPILE_${FEATURE})
|
if (NOT DEFINED COMPILE_${FEATURE})
|
||||||
|
message(STATUS "Performing Test ${FEATURE}")
|
||||||
if(CMAKE_CROSSCOMPILING)
|
if(CMAKE_CROSSCOMPILING)
|
||||||
message(STATUS "Cross-compiling to test ${FEATURE}")
|
|
||||||
try_compile(COMPILE_${FEATURE}
|
try_compile(COMPILE_${FEATURE}
|
||||||
${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${FILE}.cpp
|
${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${FILE}.cpp
|
||||||
CXX_STANDARD 17
|
CMAKE_FLAGS ${BENCHMARK_CXX_LINKER_FLAGS}
|
||||||
CXX_STANDARD_REQUIRED ON
|
LINK_LIBRARIES ${BENCHMARK_CXX_LIBRARIES})
|
||||||
CMAKE_FLAGS ${FEATURE_CHECK_CMAKE_FLAGS}
|
|
||||||
LINK_LIBRARIES ${BENCHMARK_CXX_LIBRARIES}
|
|
||||||
OUTPUT_VARIABLE COMPILE_OUTPUT_VAR)
|
|
||||||
if(COMPILE_${FEATURE})
|
if(COMPILE_${FEATURE})
|
||||||
message(WARNING
|
message(WARNING
|
||||||
"If you see build failures due to cross compilation, try setting HAVE_${VAR} to 0")
|
"If you see build failures due to cross compilation, try setting HAVE_${VAR} to 0")
|
||||||
set(RUN_${FEATURE} 0 CACHE INTERNAL "")
|
set(RUN_${FEATURE} 0)
|
||||||
else()
|
else()
|
||||||
set(RUN_${FEATURE} 1 CACHE INTERNAL "")
|
set(RUN_${FEATURE} 1)
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
message(STATUS "Compiling and running to test ${FEATURE}")
|
message(STATUS "Performing Test ${FEATURE}")
|
||||||
try_run(RUN_${FEATURE} COMPILE_${FEATURE}
|
try_run(RUN_${FEATURE} COMPILE_${FEATURE}
|
||||||
${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${FILE}.cpp
|
${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${FILE}.cpp
|
||||||
CXX_STANDARD 17
|
CMAKE_FLAGS ${BENCHMARK_CXX_LINKER_FLAGS}
|
||||||
CXX_STANDARD_REQUIRED ON
|
LINK_LIBRARIES ${BENCHMARK_CXX_LIBRARIES})
|
||||||
CMAKE_FLAGS ${FEATURE_CHECK_CMAKE_FLAGS}
|
|
||||||
LINK_LIBRARIES ${BENCHMARK_CXX_LIBRARIES}
|
|
||||||
COMPILE_OUTPUT_VARIABLE COMPILE_OUTPUT_VAR)
|
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -70,11 +56,7 @@ function(cxx_feature_check FILE)
|
|||||||
add_definitions(-DHAVE_${VAR})
|
add_definitions(-DHAVE_${VAR})
|
||||||
else()
|
else()
|
||||||
if(NOT COMPILE_${FEATURE})
|
if(NOT COMPILE_${FEATURE})
|
||||||
if(CXXFEATURECHECK_DEBUG)
|
message(STATUS "Performing Test ${FEATURE} -- failed to compile")
|
||||||
message(STATUS "Performing Test ${FEATURE} -- failed to compile: ${COMPILE_OUTPUT_VAR}")
|
|
||||||
else()
|
|
||||||
message(STATUS "Performing Test ${FEATURE} -- failed to compile")
|
|
||||||
endif()
|
|
||||||
else()
|
else()
|
||||||
message(STATUS "Performing Test ${FEATURE} -- compiled but failed to run")
|
message(STATUS "Performing Test ${FEATURE} -- compiled but failed to run")
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
-11
@@ -1,12 +1 @@
|
|||||||
@PACKAGE_INIT@
|
|
||||||
|
|
||||||
include (CMakeFindDependencyMacro)
|
|
||||||
|
|
||||||
find_dependency (Threads)
|
|
||||||
|
|
||||||
if (@BENCHMARK_ENABLE_LIBPFM@)
|
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
|
|
||||||
find_dependency (PFM)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake")
|
include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake")
|
||||||
|
|||||||
+20
-2
@@ -20,17 +20,35 @@ set(__get_git_version INCLUDED)
|
|||||||
|
|
||||||
function(get_git_version var)
|
function(get_git_version var)
|
||||||
if(GIT_EXECUTABLE)
|
if(GIT_EXECUTABLE)
|
||||||
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --match "v[0-9]*.[0-9]*.[0-9]*" --abbrev=8 --dirty
|
execute_process(COMMAND ${GIT_EXECUTABLE} describe --match "v[0-9]*.[0-9]*.[0-9]*" --abbrev=8
|
||||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
RESULT_VARIABLE status
|
RESULT_VARIABLE status
|
||||||
OUTPUT_VARIABLE GIT_VERSION
|
OUTPUT_VARIABLE GIT_VERSION
|
||||||
ERROR_QUIET)
|
ERROR_QUIET)
|
||||||
if(status)
|
if(${status})
|
||||||
set(GIT_VERSION "v0.0.0")
|
set(GIT_VERSION "v0.0.0")
|
||||||
|
else()
|
||||||
|
string(STRIP ${GIT_VERSION} GIT_VERSION)
|
||||||
|
string(REGEX REPLACE "-[0-9]+-g" "-" GIT_VERSION ${GIT_VERSION})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Work out if the repository is dirty
|
||||||
|
execute_process(COMMAND ${GIT_EXECUTABLE} update-index -q --refresh
|
||||||
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
|
OUTPUT_QUIET
|
||||||
|
ERROR_QUIET)
|
||||||
|
execute_process(COMMAND ${GIT_EXECUTABLE} diff-index --name-only HEAD --
|
||||||
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
|
OUTPUT_VARIABLE GIT_DIFF_INDEX
|
||||||
|
ERROR_QUIET)
|
||||||
|
string(COMPARE NOTEQUAL "${GIT_DIFF_INDEX}" "" GIT_DIRTY)
|
||||||
|
if (${GIT_DIRTY})
|
||||||
|
set(GIT_VERSION "${GIT_VERSION}-dirty")
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
set(GIT_VERSION "v0.0.0")
|
set(GIT_VERSION "v0.0.0")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
message(STATUS "git Version: ${GIT_VERSION}")
|
||||||
set(${var} ${GIT_VERSION} PARENT_SCOPE)
|
set(${var} ${GIT_VERSION} PARENT_SCOPE)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|||||||
-58
@@ -1,58 +0,0 @@
|
|||||||
# Download and unpack googletest at configure time
|
|
||||||
set(GOOGLETEST_PREFIX "${benchmark_BINARY_DIR}/third_party/googletest")
|
|
||||||
configure_file(${benchmark_SOURCE_DIR}/cmake/GoogleTest.cmake.in ${GOOGLETEST_PREFIX}/CMakeLists.txt @ONLY)
|
|
||||||
|
|
||||||
set(GOOGLETEST_PATH "${CMAKE_CURRENT_SOURCE_DIR}/googletest" CACHE PATH "") # Mind the quotes
|
|
||||||
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}"
|
|
||||||
-DALLOW_DOWNLOADING_GOOGLETEST=${BENCHMARK_DOWNLOAD_DEPENDENCIES} -DGOOGLETEST_PATH:PATH=${GOOGLETEST_PATH} .
|
|
||||||
RESULT_VARIABLE result
|
|
||||||
WORKING_DIRECTORY ${GOOGLETEST_PREFIX}
|
|
||||||
)
|
|
||||||
|
|
||||||
if(result)
|
|
||||||
message(FATAL_ERROR "CMake step for googletest failed: ${result}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
execute_process(
|
|
||||||
COMMAND ${CMAKE_COMMAND} --build .
|
|
||||||
RESULT_VARIABLE result
|
|
||||||
WORKING_DIRECTORY ${GOOGLETEST_PREFIX}
|
|
||||||
)
|
|
||||||
|
|
||||||
if(result)
|
|
||||||
message(FATAL_ERROR "Build step for googletest failed: ${result}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Prevent overriding the parent project's compiler/linker
|
|
||||||
# settings on Windows
|
|
||||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
|
||||||
|
|
||||||
include(${GOOGLETEST_PREFIX}/googletest-paths.cmake)
|
|
||||||
|
|
||||||
# Add googletest directly to our build. This defines
|
|
||||||
# the gtest and gtest_main targets.
|
|
||||||
add_subdirectory(${GOOGLETEST_SOURCE_DIR}
|
|
||||||
${GOOGLETEST_BINARY_DIR}
|
|
||||||
EXCLUDE_FROM_ALL)
|
|
||||||
|
|
||||||
# googletest doesn't seem to want to stay build warning clean so let's not hurt ourselves.
|
|
||||||
if (MSVC)
|
|
||||||
target_compile_options(gtest PRIVATE "/wd4244" "/wd4722")
|
|
||||||
target_compile_options(gtest_main PRIVATE "/wd4244" "/wd4722")
|
|
||||||
target_compile_options(gmock PRIVATE "/wd4244" "/wd4722")
|
|
||||||
target_compile_options(gmock_main PRIVATE "/wd4244" "/wd4722")
|
|
||||||
else()
|
|
||||||
target_compile_options(gtest PRIVATE "-w")
|
|
||||||
target_compile_options(gtest_main PRIVATE "-w")
|
|
||||||
target_compile_options(gmock PRIVATE "-w")
|
|
||||||
target_compile_options(gmock_main PRIVATE "-w")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT DEFINED GTEST_COMPILE_COMMANDS)
|
|
||||||
set(GTEST_COMPILE_COMMANDS ON)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set_target_properties(gtest PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:gtest,INTERFACE_INCLUDE_DIRECTORIES> EXPORT_COMPILE_COMMANDS ${GTEST_COMPILE_COMMANDS})
|
|
||||||
set_target_properties(gtest_main PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:gtest_main,INTERFACE_INCLUDE_DIRECTORIES> EXPORT_COMPILE_COMMANDS ${GTEST_COMPILE_COMMANDS})
|
|
||||||
set_target_properties(gmock PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:gmock,INTERFACE_INCLUDE_DIRECTORIES> EXPORT_COMPILE_COMMANDS ${GTEST_COMPILE_COMMANDS})
|
|
||||||
set_target_properties(gmock_main PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:gmock_main,INTERFACE_INCLUDE_DIRECTORIES> EXPORT_COMPILE_COMMANDS ${GTEST_COMPILE_COMMANDS})
|
|
||||||
-60
@@ -1,60 +0,0 @@
|
|||||||
cmake_minimum_required (VERSION 3.13...3.22)
|
|
||||||
|
|
||||||
project(googletest-download NONE)
|
|
||||||
|
|
||||||
# Enable ExternalProject CMake module
|
|
||||||
include(ExternalProject)
|
|
||||||
|
|
||||||
option(ALLOW_DOWNLOADING_GOOGLETEST "If googletest src tree is not found in location specified by GOOGLETEST_PATH, do fetch the archive from internet" OFF)
|
|
||||||
set(GOOGLETEST_PATH "/usr/src/googletest" CACHE PATH
|
|
||||||
"Path to the googletest root tree. Should contain googletest and googlemock subdirs. And CMakeLists.txt in root, and in both of these subdirs")
|
|
||||||
|
|
||||||
# Download and install GoogleTest
|
|
||||||
|
|
||||||
message(STATUS "Looking for Google Test sources")
|
|
||||||
message(STATUS "Looking for Google Test sources in ${GOOGLETEST_PATH}")
|
|
||||||
if(EXISTS "${GOOGLETEST_PATH}" AND IS_DIRECTORY "${GOOGLETEST_PATH}" AND EXISTS "${GOOGLETEST_PATH}/CMakeLists.txt" AND
|
|
||||||
EXISTS "${GOOGLETEST_PATH}/googletest" AND IS_DIRECTORY "${GOOGLETEST_PATH}/googletest" AND EXISTS "${GOOGLETEST_PATH}/googletest/CMakeLists.txt" AND
|
|
||||||
EXISTS "${GOOGLETEST_PATH}/googlemock" AND IS_DIRECTORY "${GOOGLETEST_PATH}/googlemock" AND EXISTS "${GOOGLETEST_PATH}/googlemock/CMakeLists.txt")
|
|
||||||
message(STATUS "Found Google Test in ${GOOGLETEST_PATH}")
|
|
||||||
|
|
||||||
ExternalProject_Add(
|
|
||||||
googletest
|
|
||||||
PREFIX "${CMAKE_BINARY_DIR}"
|
|
||||||
DOWNLOAD_DIR "${CMAKE_BINARY_DIR}/download"
|
|
||||||
SOURCE_DIR "${GOOGLETEST_PATH}" # use existing src dir.
|
|
||||||
BINARY_DIR "${CMAKE_BINARY_DIR}/build"
|
|
||||||
CONFIGURE_COMMAND ""
|
|
||||||
BUILD_COMMAND ""
|
|
||||||
INSTALL_COMMAND ""
|
|
||||||
TEST_COMMAND ""
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
if(NOT ALLOW_DOWNLOADING_GOOGLETEST)
|
|
||||||
message(SEND_ERROR "Did not find Google Test sources! Either pass correct path in GOOGLETEST_PATH, or enable BENCHMARK_DOWNLOAD_DEPENDENCIES, or disable BENCHMARK_USE_BUNDLED_GTEST, or disable BENCHMARK_ENABLE_GTEST_TESTS / BENCHMARK_ENABLE_TESTING.")
|
|
||||||
return()
|
|
||||||
else()
|
|
||||||
message(STATUS "Did not find Google Test sources! Fetching from web...")
|
|
||||||
ExternalProject_Add(
|
|
||||||
googletest
|
|
||||||
GIT_REPOSITORY https://github.com/google/googletest.git
|
|
||||||
GIT_TAG "v1.15.2"
|
|
||||||
GIT_SHALLOW "ON"
|
|
||||||
PREFIX "${CMAKE_BINARY_DIR}"
|
|
||||||
STAMP_DIR "${CMAKE_BINARY_DIR}/stamp"
|
|
||||||
DOWNLOAD_DIR "${CMAKE_BINARY_DIR}/download"
|
|
||||||
SOURCE_DIR "${CMAKE_BINARY_DIR}/src"
|
|
||||||
BINARY_DIR "${CMAKE_BINARY_DIR}/build"
|
|
||||||
CONFIGURE_COMMAND ""
|
|
||||||
BUILD_COMMAND ""
|
|
||||||
INSTALL_COMMAND ""
|
|
||||||
TEST_COMMAND ""
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
ExternalProject_Get_Property(googletest SOURCE_DIR BINARY_DIR)
|
|
||||||
file(WRITE googletest-paths.cmake
|
|
||||||
"set(GOOGLETEST_SOURCE_DIR \"${SOURCE_DIR}\")
|
|
||||||
set(GOOGLETEST_BINARY_DIR \"${BINARY_DIR}\")
|
|
||||||
")
|
|
||||||
+113
@@ -0,0 +1,113 @@
|
|||||||
|
|
||||||
|
include(split_list)
|
||||||
|
|
||||||
|
macro(build_external_gtest)
|
||||||
|
include(ExternalProject)
|
||||||
|
set(GTEST_FLAGS "")
|
||||||
|
if (BENCHMARK_USE_LIBCXX)
|
||||||
|
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||||
|
list(APPEND GTEST_FLAGS -stdlib=libc++)
|
||||||
|
else()
|
||||||
|
message(WARNING "Unsupported compiler (${CMAKE_CXX_COMPILER}) when using libc++")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
if (BENCHMARK_BUILD_32_BITS)
|
||||||
|
list(APPEND GTEST_FLAGS -m32)
|
||||||
|
endif()
|
||||||
|
if (NOT "${CMAKE_CXX_FLAGS}" STREQUAL "")
|
||||||
|
list(APPEND GTEST_FLAGS ${CMAKE_CXX_FLAGS})
|
||||||
|
endif()
|
||||||
|
string(TOUPPER "${CMAKE_BUILD_TYPE}" GTEST_BUILD_TYPE)
|
||||||
|
if ("${GTEST_BUILD_TYPE}" STREQUAL "COVERAGE")
|
||||||
|
set(GTEST_BUILD_TYPE "DEBUG")
|
||||||
|
endif()
|
||||||
|
# FIXME: Since 10/Feb/2017 the googletest trunk has had a bug where
|
||||||
|
# -Werror=unused-function fires during the build on OS X. This is a temporary
|
||||||
|
# workaround to keep our travis bots from failing. It should be removed
|
||||||
|
# once gtest is fixed.
|
||||||
|
if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||||
|
list(APPEND GTEST_FLAGS "-Wno-unused-function")
|
||||||
|
endif()
|
||||||
|
split_list(GTEST_FLAGS)
|
||||||
|
set(EXCLUDE_FROM_ALL_OPT "")
|
||||||
|
set(EXCLUDE_FROM_ALL_VALUE "")
|
||||||
|
if (${CMAKE_VERSION} VERSION_GREATER "3.0.99")
|
||||||
|
set(EXCLUDE_FROM_ALL_OPT "EXCLUDE_FROM_ALL")
|
||||||
|
set(EXCLUDE_FROM_ALL_VALUE "ON")
|
||||||
|
endif()
|
||||||
|
ExternalProject_Add(googletest
|
||||||
|
${EXCLUDE_FROM_ALL_OPT} ${EXCLUDE_FROM_ALL_VALUE}
|
||||||
|
GIT_REPOSITORY https://github.com/google/googletest.git
|
||||||
|
GIT_TAG master
|
||||||
|
PREFIX "${CMAKE_BINARY_DIR}/googletest"
|
||||||
|
INSTALL_DIR "${CMAKE_BINARY_DIR}/googletest"
|
||||||
|
CMAKE_CACHE_ARGS
|
||||||
|
-DCMAKE_BUILD_TYPE:STRING=${GTEST_BUILD_TYPE}
|
||||||
|
-DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER}
|
||||||
|
-DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER}
|
||||||
|
-DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
||||||
|
-DCMAKE_INSTALL_LIBDIR:PATH=<INSTALL_DIR>/lib
|
||||||
|
-DCMAKE_CXX_FLAGS:STRING=${GTEST_FLAGS}
|
||||||
|
-Dgtest_force_shared_crt:BOOL=ON
|
||||||
|
)
|
||||||
|
|
||||||
|
ExternalProject_Get_Property(googletest install_dir)
|
||||||
|
set(GTEST_INCLUDE_DIRS ${install_dir}/include)
|
||||||
|
file(MAKE_DIRECTORY ${GTEST_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
set(LIB_SUFFIX "${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
||||||
|
set(LIB_PREFIX "${CMAKE_STATIC_LIBRARY_PREFIX}")
|
||||||
|
if("${GTEST_BUILD_TYPE}" STREQUAL "DEBUG")
|
||||||
|
set(LIB_SUFFIX "d${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Use gmock_main instead of gtest_main because it initializes gtest as well.
|
||||||
|
# Note: The libraries are listed in reverse order of their dependancies.
|
||||||
|
foreach(LIB gtest gmock gmock_main)
|
||||||
|
add_library(${LIB} UNKNOWN IMPORTED)
|
||||||
|
set_target_properties(${LIB} PROPERTIES
|
||||||
|
IMPORTED_LOCATION ${install_dir}/lib/${LIB_PREFIX}${LIB}${LIB_SUFFIX}
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES ${GTEST_INCLUDE_DIRS}
|
||||||
|
INTERFACE_LINK_LIBRARIES "${GTEST_BOTH_LIBRARIES}"
|
||||||
|
)
|
||||||
|
add_dependencies(${LIB} googletest)
|
||||||
|
list(APPEND GTEST_BOTH_LIBRARIES ${LIB})
|
||||||
|
endforeach()
|
||||||
|
endmacro(build_external_gtest)
|
||||||
|
|
||||||
|
if (BENCHMARK_ENABLE_GTEST_TESTS)
|
||||||
|
if (IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/googletest)
|
||||||
|
set(GTEST_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/googletest")
|
||||||
|
set(INSTALL_GTEST OFF CACHE INTERNAL "")
|
||||||
|
set(INSTALL_GMOCK OFF CACHE INTERNAL "")
|
||||||
|
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/googletest)
|
||||||
|
set(GTEST_BOTH_LIBRARIES gtest gmock gmock_main)
|
||||||
|
foreach(HEADER test mock)
|
||||||
|
# CMake 2.8 and older don't respect INTERFACE_INCLUDE_DIRECTORIES, so we
|
||||||
|
# have to add the paths ourselves.
|
||||||
|
set(HFILE g${HEADER}/g${HEADER}.h)
|
||||||
|
set(HPATH ${GTEST_ROOT}/google${HEADER}/include)
|
||||||
|
find_path(HEADER_PATH_${HEADER} ${HFILE}
|
||||||
|
NO_DEFAULT_PATHS
|
||||||
|
HINTS ${HPATH}
|
||||||
|
)
|
||||||
|
if (NOT HEADER_PATH_${HEADER})
|
||||||
|
message(FATAL_ERROR "Failed to find header ${HFILE} in ${HPATH}")
|
||||||
|
endif()
|
||||||
|
list(APPEND GTEST_INCLUDE_DIRS ${HEADER_PATH_${HEADER}})
|
||||||
|
endforeach()
|
||||||
|
elseif(BENCHMARK_DOWNLOAD_DEPENDENCIES)
|
||||||
|
build_external_gtest()
|
||||||
|
else()
|
||||||
|
find_package(GTest REQUIRED)
|
||||||
|
find_path(GMOCK_INCLUDE_DIRS gmock/gmock.h
|
||||||
|
HINTS ${GTEST_INCLUDE_DIRS})
|
||||||
|
if (NOT GMOCK_INCLUDE_DIRS)
|
||||||
|
message(FATAL_ERROR "Failed to find header gmock/gmock.h with hint ${GTEST_INCLUDE_DIRS}")
|
||||||
|
endif()
|
||||||
|
set(GTEST_INCLUDE_DIRS ${GTEST_INCLUDE_DIRS} ${GMOCK_INCLUDE_DIRS})
|
||||||
|
# FIXME: We don't currently require the gmock library to build the tests,
|
||||||
|
# and it's likely we won't find it, so we don't try. As long as we've
|
||||||
|
# found the gmock/gmock.h header and gtest_main that should be good enough.
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
+3
-4
@@ -1,12 +1,11 @@
|
|||||||
prefix=@CMAKE_INSTALL_PREFIX@
|
prefix=@CMAKE_INSTALL_PREFIX@
|
||||||
exec_prefix=${prefix}
|
exec_prefix=${prefix}
|
||||||
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
|
libdir=${prefix}/lib
|
||||||
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
|
includedir=${prefix}/include
|
||||||
|
|
||||||
Name: @PROJECT_NAME@
|
Name: @PROJECT_NAME@
|
||||||
Description: Google microbenchmark framework
|
Description: Google microbenchmark framework
|
||||||
Version: @NORMALIZED_VERSION@
|
Version: @VERSION@
|
||||||
|
|
||||||
Libs: -L${libdir} -lbenchmark
|
Libs: -L${libdir} -lbenchmark
|
||||||
Libs.private: -lpthread @BENCHMARK_PRIVATE_LINK_LIBRARIES@
|
|
||||||
Cflags: -I${includedir}
|
Cflags: -I${includedir}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
|
|
||||||
|
|
||||||
Name: @PROJECT_NAME@
|
|
||||||
Description: Google microbenchmark framework (with main() function)
|
|
||||||
Version: @NORMALIZED_VERSION@
|
|
||||||
Requires: benchmark
|
|
||||||
Libs: -L${libdir} -lbenchmark_main
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user