Compare commits
21
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37d0bd71db | ||
|
|
000faeaca7 | ||
|
|
3f9783ac02 | ||
|
|
4e6dc67ae8 | ||
|
|
666140bb73 | ||
|
|
56050de5cd | ||
|
|
772136decd | ||
|
|
f664601c51 | ||
|
|
f06b27b7fb | ||
|
|
ef53ce88d4 | ||
|
|
ef18030e1a | ||
|
|
f07176c0a2 | ||
|
|
15db141c7a | ||
|
|
d4bbb7c591 | ||
|
|
92e620d2ad | ||
|
|
311104da97 | ||
|
|
3127632f96 | ||
|
|
59f611bfde | ||
|
|
2d556bdca2 | ||
|
|
65e7dba8b8 | ||
|
|
6550a63056 |
@@ -6,7 +6,3 @@
|
|||||||
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.0'
|
implementation 'com.google.android.filament:filament-android:1.67.1'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -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.0'
|
pod 'Filament', '~> 1.67.1'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|||||||
@@ -7,6 +7,12 @@ 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,6 +16,9 @@
|
|||||||
|
|
||||||
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
|
||||||
@@ -26,6 +29,7 @@ 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
|
||||||
@@ -119,6 +123,8 @@ 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()
|
||||||
@@ -305,10 +311,39 @@ 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.0
|
VERSION_NAME=1.67.1
|
||||||
|
|
||||||
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.66.2'
|
implementation 'com.google.android.filament:filament-android:1.67.1'
|
||||||
}
|
}
|
||||||
</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.66.2'
|
<pre><code class="language-shell">pod 'Filament', '~> 1.67.1'
|
||||||
</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>
|
||||||
|
|||||||
+1821
-1790
File diff suppressed because it is too large
Load Diff
+1201
-1174
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,13 +19,17 @@
|
|||||||
#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 {
|
||||||
|
|
||||||
@@ -508,13 +512,22 @@ public:
|
|||||||
// --------------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------------
|
||||||
// Debugging APIs
|
// Debugging APIs
|
||||||
|
|
||||||
using DebugUpdateStatFunc = utils::Invocable<void(const char* UTILS_NONNULL key, uint64_t value)>;
|
using DebugUpdateStatFunc = utils::Invocable<void(const char* UTILS_NONNULL key,
|
||||||
|
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;
|
||||||
@@ -533,15 +546,32 @@ 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 value the updated value of key
|
* @param intValue the updated integer value of key (the string value passed to the
|
||||||
|
* callback will be empty)
|
||||||
*/
|
*/
|
||||||
void debugUpdateStat(const char* UTILS_NONNULL key, uint64_t value);
|
void debugUpdateStat(const char* UTILS_NONNULL key, uint64_t intValue);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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:
|
||||||
InsertBlobFunc mInsertBlob;
|
std::shared_ptr<InsertBlobFunc> mInsertBlob;
|
||||||
RetrieveBlobFunc mRetrieveBlob;
|
std::shared_ptr<RetrieveBlobFunc> mRetrieveBlob;
|
||||||
DebugUpdateStatFunc mDebugUpdateStat;
|
std::shared_ptr<DebugUpdateStatFunc> mDebugUpdateStat;
|
||||||
|
mutable utils::Mutex mMutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace filament
|
} // namespace filament
|
||||||
|
|||||||
@@ -17,8 +17,6 @@
|
|||||||
#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>
|
||||||
@@ -171,19 +169,10 @@ protected:
|
|||||||
SwapChain* drawSwapChain,
|
SwapChain* drawSwapChain,
|
||||||
SwapChain* readSwapChain) override;
|
SwapChain* readSwapChain) override;
|
||||||
|
|
||||||
struct 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;
|
|
||||||
private:
|
|
||||||
AndroidSwapChainHelper mImpl{};
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct SwapChainEGLAndroid;
|
||||||
|
struct AndroidDetails;
|
||||||
|
|
||||||
// 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;
|
||||||
@@ -203,17 +192,14 @@ 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;
|
||||||
AndroidProducerThrottling mProducerThrottling;
|
SwapChainEGLAndroid* mCurrentDrawSwapChain{};
|
||||||
bool mAssertNativeWindowIsValid = false;
|
bool mAssertNativeWindowIsValid = false;
|
||||||
|
|
||||||
AndroidFrameCallback mAndroidFrameCallback;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace filament::backend
|
} // namespace filament::backend
|
||||||
|
|||||||
@@ -17,12 +17,13 @@
|
|||||||
#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 {
|
||||||
@@ -91,6 +92,8 @@ 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;
|
||||||
@@ -99,7 +102,7 @@ private:
|
|||||||
~ExternalImageVulkanAndroid() override;
|
~ExternalImageVulkanAndroid() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
AndroidFrameCallback mAndroidFrameCallback;
|
AndroidDetails& mAndroidDetails;
|
||||||
int mOSVersion{};
|
int mOSVersion{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,16 @@ 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,6 +32,7 @@ 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,
|
||||||
@@ -51,6 +52,7 @@ 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 "AndroidNdk.h"
|
#include <backend/platforms/AndroidNdk.h>
|
||||||
|
|
||||||
#include <android/hardware_buffer.h>
|
#include <android/hardware_buffer.h>
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,9 @@
|
|||||||
|
|
||||||
#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>
|
||||||
@@ -36,11 +39,23 @@ 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);
|
||||||
auto const pos = mFrameIdToSystemFrameId.find(frameId);
|
// frameIds must be strictly monotonic, if that's not the case (i.e. the new frameId is
|
||||||
if (pos && *pos != sysFrameId) {
|
// less or equal to the last one in the map), we have to clear the map, because the
|
||||||
// we're trying to associate the same frame id to a different frame!
|
// map's find() assume sorted keys.
|
||||||
return false;
|
// This case can happen if two different filament::Renderer are used with the same
|
||||||
|
// 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,42 +139,73 @@ bool Platform::queryFrameTimestamps(SwapChain const*, uint64_t, FrameTimestamps*
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Platform::setBlobFunc(InsertBlobFunc&& insertBlob, RetrieveBlobFunc&& retrieveBlob) noexcept {
|
void Platform::setBlobFunc(InsertBlobFunc&& insertBlob, RetrieveBlobFunc&& retrieveBlob) noexcept {
|
||||||
mInsertBlob = std::move(insertBlob);
|
std::lock_guard<decltype(mMutex)> lock(mMutex);
|
||||||
mRetrieveBlob = std::move(retrieveBlob);
|
mInsertBlob = std::make_shared<InsertBlobFunc>(std::move(insertBlob));
|
||||||
|
mRetrieveBlob = std::make_shared<RetrieveBlobFunc>(std::move(retrieveBlob));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Platform::hasInsertBlobFunc() const noexcept {
|
bool Platform::hasInsertBlobFunc() const noexcept {
|
||||||
return bool(mInsertBlob);
|
std::lock_guard<decltype(mMutex)> lock(mMutex);
|
||||||
|
return mInsertBlob && bool(*mInsertBlob);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Platform::hasRetrieveBlobFunc() const noexcept {
|
bool Platform::hasRetrieveBlobFunc() const noexcept {
|
||||||
return bool(mRetrieveBlob);
|
std::lock_guard<decltype(mMutex)> lock(mMutex);
|
||||||
|
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) {
|
||||||
if (mInsertBlob) {
|
std::shared_ptr<InsertBlobFunc> callback;
|
||||||
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) {
|
||||||
if (mRetrieveBlob) {
|
std::shared_ptr<RetrieveBlobFunc> callback;
|
||||||
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 {
|
||||||
mDebugUpdateStat = std::move(debugUpdateStat);
|
std::lock_guard<decltype(mMutex)> lock(mMutex);
|
||||||
|
mDebugUpdateStat = std::make_shared<DebugUpdateStatFunc>(std::move(debugUpdateStat));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Platform::hasDebugUpdateStatFunc() const noexcept {
|
bool Platform::hasDebugUpdateStatFunc() const noexcept {
|
||||||
return bool(mDebugUpdateStat);
|
std::lock_guard<decltype(mMutex)> lock(mMutex);
|
||||||
|
return mDebugUpdateStat && bool(*mDebugUpdateStat);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Platform::debugUpdateStat(const char* key, uint64_t value) {
|
void Platform::debugUpdateStat(const char* key, uint64_t intValue) {
|
||||||
if (mDebugUpdateStat) {
|
std::shared_ptr<DebugUpdateStatFunc> callback;
|
||||||
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 || mCpuBuffer; }
|
[[nodiscard]] bool wasAllocationSuccessful() const noexcept { return mBuffer; }
|
||||||
|
|
||||||
MetalBuffer(const MetalBuffer& rhs) = delete;
|
MetalBuffer(const MetalBuffer& rhs) = delete;
|
||||||
MetalBuffer& operator=(const MetalBuffer& rhs) = delete;
|
MetalBuffer& operator=(const MetalBuffer& rhs) = delete;
|
||||||
@@ -185,14 +185,12 @@ 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, or nil if there
|
* @return The MTLBuffer representing the current state of the buffer to bind, it never returns
|
||||||
* is no device allocation.
|
* nil.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
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()) {
|
||||||
@@ -235,7 +233,6 @@ 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,34 +39,30 @@ MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType,
|
|||||||
mUploadStrategy = UploadStrategy::POOL;
|
mUploadStrategy = UploadStrategy::POOL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the buffer is less than 4K in size and is updated frequently, we don't use an explicit
|
MTLResourceOptions options = MTLResourceStorageModePrivate;
|
||||||
// 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 (size <= 4 * 1024 && bindingType != BufferObjectBinding::SHADER_STORAGE &&
|
if (any(usage & BufferUsage::SHARED_WRITE_BIT)) {
|
||||||
usage == BufferUsage::DYNAMIC && !forceGpuBuffer) {
|
#if defined(FILAMENT_IOS) || defined(__arm64__) || defined(__aarch64__)
|
||||||
mBuffer = nil;
|
// iOS and Apple Silicon devices use UMA (Unified Memory Architecture), so we use Shared memory.
|
||||||
mCpuBuffer = malloc(size);
|
options = MTLResourceStorageModeShared;
|
||||||
return;
|
#else
|
||||||
|
// 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:MTLResourceStorageModePrivate],
|
mBuffer = { [context.device newBufferWithLength:size options:options],
|
||||||
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() {
|
MetalBuffer::~MetalBuffer() = default;
|
||||||
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) {
|
||||||
@@ -83,12 +79,6 @@ 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));
|
||||||
@@ -106,11 +96,6 @@ 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();
|
||||||
}
|
}
|
||||||
@@ -171,41 +156,6 @@ 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,6 +17,7 @@
|
|||||||
#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"
|
||||||
@@ -129,6 +130,7 @@ 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,6 +153,7 @@ 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,6 +61,8 @@ public:
|
|||||||
|
|
||||||
MetalContext* getContext() { return mContext; }
|
MetalContext* getContext() { return mContext; }
|
||||||
|
|
||||||
|
using DriverBase::scheduleDestroy;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
friend class MetalSwapChain;
|
friend class MetalSwapChain;
|
||||||
|
|||||||
@@ -254,6 +254,18 @@ 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,
|
||||||
@@ -2263,7 +2275,10 @@ 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) {
|
||||||
construct_handle<MetalMemoryMappedBuffer>(mmbh, boh, offset, size, access);
|
assert_invariant(boh);
|
||||||
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2271,21 +2286,16 @@ 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
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* 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,14 +569,22 @@ 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{};
|
||||||
uint32_t size = 0;
|
struct {
|
||||||
uint32_t offset = 0;
|
MetalBufferObject* bo;
|
||||||
|
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,5 +1664,44 @@ 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 = mtl.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
|
||||||
|
|||||||
@@ -735,7 +735,7 @@ void OpenGLContext::bindVertexArray(RenderPrimitive const* p) noexcept {
|
|||||||
// - the nameVersion is out of date *and* we're on the protected context, in this case:
|
// - the nameVersion is out of date *and* we're on the protected context, in this case:
|
||||||
// - the name must be stale from a previous use of this context because we always
|
// - the name must be stale from a previous use of this context because we always
|
||||||
// destroy the protected context when we're done with it.
|
// destroy the protected context when we're done with it.
|
||||||
bool const recreateVaoName = p != &mDefaultVAO &&
|
bool const recreateVaoName = vao != &mDefaultVAO &&
|
||||||
((vao->vao[contextIndex] == 0) ||
|
((vao->vao[contextIndex] == 0) ||
|
||||||
(vao->nameVersion != state.age && contextIndex == 1));
|
(vao->nameVersion != state.age && contextIndex == 1));
|
||||||
if (UTILS_UNLIKELY(recreateVaoName)) {
|
if (UTILS_UNLIKELY(recreateVaoName)) {
|
||||||
|
|||||||
@@ -28,14 +28,13 @@
|
|||||||
#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>
|
||||||
@@ -99,6 +98,27 @@ 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.
|
||||||
@@ -112,17 +132,20 @@ 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 = default;
|
PlatformEGLAndroid::~PlatformEGLAndroid() noexcept {
|
||||||
|
delete &mAndroidDetails;
|
||||||
|
}
|
||||||
|
|
||||||
void PlatformEGLAndroid::terminate() noexcept {
|
void PlatformEGLAndroid::terminate() noexcept {
|
||||||
mAndroidFrameCallback.terminate();
|
mAndroidDetails.androidFrameCallback.terminate();
|
||||||
ExternalStreamManagerAndroid::destroy(&mExternalStreamManager);
|
ExternalStreamManagerAndroid::destroy(&mExternalStreamManager);
|
||||||
PlatformEGL::terminate();
|
PlatformEGL::terminate();
|
||||||
}
|
}
|
||||||
@@ -207,9 +230,9 @@ Driver* PlatformEGLAndroid::createDriver(void* sharedContext,
|
|||||||
"eglGetNativeClientBufferANDROID"));
|
"eglGetNativeClientBufferANDROID"));
|
||||||
|
|
||||||
if (ext.egl.ANDROID_presentation_time) {
|
if (ext.egl.ANDROID_presentation_time) {
|
||||||
eglGetNativeClientBufferANDROID =
|
eglPresentationTimeANDROID =
|
||||||
PFNEGLGETNATIVECLIENTBUFFERANDROIDPROC(eglGetProcAddress(
|
PFNEGLPRESENTATIONTIMEANDROIDPROC(eglGetProcAddress(
|
||||||
"eglGetNativeClientBufferANDROID"));
|
"eglPresentationTimeANDROID"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ext.egl.ANDROID_get_frame_timestamps) {
|
if (ext.egl.ANDROID_get_frame_timestamps) {
|
||||||
@@ -240,7 +263,7 @@ Driver* PlatformEGLAndroid::createDriver(void* sharedContext,
|
|||||||
|
|
||||||
mAssertNativeWindowIsValid = driverConfig.assertNativeWindowIsValid;
|
mAssertNativeWindowIsValid = driverConfig.assertNativeWindowIsValid;
|
||||||
|
|
||||||
mAndroidFrameCallback.init();
|
mAndroidDetails.androidFrameCallback.init();
|
||||||
|
|
||||||
return driver;
|
return driver;
|
||||||
}
|
}
|
||||||
@@ -264,15 +287,25 @@ bool PlatformEGLAndroid::queryCompositorTiming(SwapChain const* swapchain,
|
|||||||
}
|
}
|
||||||
|
|
||||||
AndroidFrameCallback::Timeline const preferredTimeline{
|
AndroidFrameCallback::Timeline const preferredTimeline{
|
||||||
mAndroidFrameCallback.getPreferredTimeline() };
|
mAndroidDetails.androidFrameCallback.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 false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<EGLnsecsANDROID, 3> values;
|
std::array<EGLnsecsANDROID, 3> values;
|
||||||
@@ -283,26 +316,16 @@ 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 (!success) {
|
if (UTILS_UNLIKELY(!success)) {
|
||||||
return false;
|
// reset current error to EGL_SUCCESS
|
||||||
|
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,
|
||||||
@@ -327,6 +350,10 @@ 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) {
|
||||||
@@ -347,7 +374,9 @@ 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 (!success) {
|
if (UTILS_UNLIKELY(!success)) {
|
||||||
|
// reset current error to EGL_SUCCESS
|
||||||
|
eglGetError();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
outFrameTimestamps->requestedPresentTime = values[0];
|
outFrameTimestamps->requestedPresentTime = values[0];
|
||||||
@@ -361,28 +390,44 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -675,12 +720,12 @@ AcquiredImage PlatformEGLAndroid::transformAcquiredImage(AcquiredImage const sou
|
|||||||
|
|
||||||
|
|
||||||
bool PlatformEGLAndroid::isProducerThrottlingControlSupported() const {
|
bool PlatformEGLAndroid::isProducerThrottlingControlSupported() const {
|
||||||
return mProducerThrottling.isSupported();
|
return mAndroidDetails.producerThrottling.isSupported();
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t PlatformEGLAndroid::setProducerThrottlingEnabled(
|
int32_t PlatformEGLAndroid::setProducerThrottlingEnabled(
|
||||||
EGLNativeWindowType const nativeWindow, bool const enabled) const {
|
EGLNativeWindowType const nativeWindow, bool const enabled) const {
|
||||||
return mProducerThrottling.setProducerThrottlingEnabled(nativeWindow, enabled);
|
return mAndroidDetails.producerThrottling.setProducerThrottlingEnabled(nativeWindow, enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------
|
||||||
@@ -704,8 +749,6 @@ 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,14 +921,6 @@ 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,10 +48,6 @@
|
|||||||
#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;
|
||||||
@@ -206,9 +202,6 @@ 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
|
||||||
|
|||||||
@@ -191,9 +191,12 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
|||||||
.alphaToCoverageEnable = raster.alphaToCoverageEnable,
|
.alphaToCoverageEnable = raster.alphaToCoverageEnable,
|
||||||
.alphaToOneEnable = VK_FALSE,
|
.alphaToOneEnable = VK_FALSE,
|
||||||
};
|
};
|
||||||
|
bool const enableDepthTest =
|
||||||
|
raster.depthCompareOp != SamplerCompareFunc::A ||
|
||||||
|
raster.depthWriteEnable;
|
||||||
VkPipelineDepthStencilStateCreateInfo vkDs = {
|
VkPipelineDepthStencilStateCreateInfo vkDs = {
|
||||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
||||||
.depthTestEnable = VK_TRUE,
|
.depthTestEnable = enableDepthTest ? VK_TRUE : VK_FALSE,
|
||||||
.depthWriteEnable = raster.depthWriteEnable,
|
.depthWriteEnable = raster.depthWriteEnable,
|
||||||
.depthCompareOp = fvkutils::getCompareOp(raster.depthCompareOp),
|
.depthCompareOp = fvkutils::getCompareOp(raster.depthCompareOp),
|
||||||
.depthBoundsTestEnable = VK_FALSE,
|
.depthBoundsTestEnable = VK_FALSE,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
* 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"
|
||||||
@@ -20,6 +21,8 @@
|
|||||||
#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>
|
||||||
@@ -32,6 +35,7 @@
|
|||||||
#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;
|
||||||
@@ -200,6 +204,14 @@ 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) {
|
||||||
@@ -208,14 +220,19 @@ 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 = default;
|
VulkanPlatformAndroid::~VulkanPlatformAndroid() noexcept {
|
||||||
|
delete &mAndroidDetails;
|
||||||
|
}
|
||||||
|
|
||||||
Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage(
|
Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage(
|
||||||
AHardwareBuffer const* buffer, bool sRGB) noexcept {
|
AHardwareBuffer const* buffer, bool sRGB) noexcept {
|
||||||
@@ -497,14 +514,14 @@ int VulkanPlatformAndroid::getOSVersion() const noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VulkanPlatformAndroid::terminate() {
|
void VulkanPlatformAndroid::terminate() {
|
||||||
mAndroidFrameCallback.terminate();
|
mAndroidDetails.androidFrameCallback.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) {
|
||||||
mAndroidFrameCallback.init();
|
mAndroidDetails.androidFrameCallback.init();
|
||||||
}
|
}
|
||||||
return driver;
|
return driver;
|
||||||
}
|
}
|
||||||
@@ -520,13 +537,19 @@ bool VulkanPlatformAndroid::queryCompositorTiming(SwapChain const* swapchain,
|
|||||||
}
|
}
|
||||||
|
|
||||||
AndroidFrameCallback::Timeline const preferredTimeline{
|
AndroidFrameCallback::Timeline const preferredTimeline{
|
||||||
mAndroidFrameCallback.getPreferredTimeline() };
|
mAndroidDetails.androidFrameCallback.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);
|
||||||
return vulkanSwapchain->queryCompositorTiming(outCompositorTiming);
|
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,6 +165,11 @@ 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 = {};
|
||||||
@@ -356,13 +361,15 @@ bool VulkanPlatformSurfaceSwapChain::queryCompositorTiming(
|
|||||||
CompositorTiming* outCompositorTiming) const {
|
CompositorTiming* outCompositorTiming) const {
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
// fallback to private APIs
|
// fallback to private APIs
|
||||||
int const status = NativeWindow::getCompositorTiming(
|
if (UTILS_VERY_LIKELY(mNativeWindow)) {
|
||||||
static_cast<ANativeWindow*>(mNativeWindow),
|
int const status = NativeWindow::getCompositorTiming(
|
||||||
&outCompositorTiming->compositeDeadline,
|
static_cast<ANativeWindow*>(mNativeWindow),
|
||||||
&outCompositorTiming->compositeInterval,
|
&outCompositorTiming->compositeDeadline,
|
||||||
&outCompositorTiming->compositeToPresentLatency);
|
&outCompositorTiming->compositeInterval,
|
||||||
if (status == 0) {
|
&outCompositorTiming->compositeToPresentLatency);
|
||||||
return true;
|
if (status == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return VulkanPlatformSwapChainBase::queryCompositorTiming(outCompositorTiming);
|
return VulkanPlatformSwapChainBase::queryCompositorTiming(outCompositorTiming);
|
||||||
|
|||||||
@@ -25,7 +25,8 @@
|
|||||||
#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>
|
||||||
@@ -122,6 +123,7 @@ private:
|
|||||||
|
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
AndroidSwapChainHelper mImpl{};
|
AndroidSwapChainHelper mImpl{};
|
||||||
|
AndroidProducerThrottling mProducerThrottling;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -113,8 +113,10 @@ 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;
|
||||||
@@ -326,6 +328,8 @@ 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
|
||||||
@@ -337,6 +341,8 @@ 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,6 +16,7 @@
|
|||||||
|
|
||||||
#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>
|
||||||
|
|
||||||
@@ -169,6 +170,7 @@ 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,8 +718,7 @@ 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;
|
||||||
@@ -758,7 +757,7 @@ void FEngine::gc() {
|
|||||||
void FEngine::submitFrame() {
|
void FEngine::submitFrame() {
|
||||||
if (isUboBatchingEnabled()) {
|
if (isUboBatchingEnabled()) {
|
||||||
DriverApi& driver = getDriverApi();
|
DriverApi& driver = getDriverApi();
|
||||||
getUboManager()->endFrame(driver, getMaterialInstanceResourceList());
|
getUboManager()->endFrame(driver);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1285,11 +1284,6 @@ 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,6 +95,7 @@ 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() });
|
||||||
@@ -167,6 +168,7 @@ 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() });
|
||||||
@@ -211,6 +213,10 @@ 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,6 +48,7 @@ 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;
|
||||||
|
|
||||||
@@ -115,8 +116,7 @@ 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(materialInstances) == SUCCESS) {
|
if (allocateOnDemand() == 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,25 +135,19 @@ 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(materialInstances);
|
const allocation_size_t requiredSize = calculateRequiredSize();
|
||||||
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(materialInstances);
|
allocateAllInstances();
|
||||||
|
|
||||||
// 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& materialInstance : materialInstances) {
|
for (const auto* mi : mManagedInstances) {
|
||||||
materialInstance.second.forEach([](const FMaterialInstance* mi) {
|
mi->getUniformBuffer().invalidate();
|
||||||
if (!mi->isUsingUboBatching()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mi->getUniformBuffer().invalidate();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,24 +158,16 @@ 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& materialInstance : materialInstances) {
|
for (const auto* mi : mManagedInstances) {
|
||||||
materialInstance.second.forEach([&allocator, &allocationIds](const FMaterialInstance* mi) {
|
const AllocationId id = mi->getAllocationId();
|
||||||
if (!mi->isUsingUboBatching()) {
|
if (UTILS_UNLIKELY(!BufferAllocator::isValid(id))) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AllocationId id = mi->getAllocationId();
|
mAllocator.acquireGpu(id);
|
||||||
if (!BufferAllocator::isValid(id)) {
|
allocationIds.insert(id);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
allocator.acquireGpu(id);
|
|
||||||
allocationIds.insert(id);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mFenceManager.track(driver, std::move(allocationIds));
|
mFenceManager.track(driver, std::move(allocationIds));
|
||||||
@@ -194,76 +180,90 @@ 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::retireSlot(BufferAllocator::AllocationId id) {
|
void UboManager::manageMaterialInstance(FMaterialInstance* instance) {
|
||||||
if (!BufferAllocator::isValid(id))
|
mPendingInstances.insert(instance);
|
||||||
return;
|
|
||||||
mAllocator.retire(id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UboManager::AllocationResult UboManager::allocateOnDemand(
|
void UboManager::unmanageMaterialInstance(FMaterialInstance* materialInstance) {
|
||||||
const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>&
|
AllocationId id = materialInstance->getAllocationId();
|
||||||
materialInstances) {
|
mPendingInstances.erase(materialInstance);
|
||||||
// Collect all MIs that need allocation into two groups.
|
mManagedInstances.erase(materialInstance);
|
||||||
std::vector<FMaterialInstance*> newInstances;
|
|
||||||
std::vector<FMaterialInstance*> existingInstances;
|
if (!BufferAllocator::isValid(id)) {
|
||||||
for (const auto& [_, miList] : materialInstances) {
|
return;
|
||||||
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 (FMaterialInstance* mi : newInstances) {
|
for (auto* mi : mPendingInstances) {
|
||||||
|
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 (FMaterialInstance* mi : existingInstances) {
|
for (auto* mi: mManagedInstances) {
|
||||||
if (mi->getUniformBuffer().isDirty() && mAllocator.isLockedByGpu(mi->getAllocationId())) {
|
if (!BufferAllocator::isValid(mi->getAllocationId())) {
|
||||||
mAllocator.retire(mi->getAllocationId());
|
continue;
|
||||||
auto [newId, newOffset] = mAllocator.allocate(mi->getUniformBuffer().getSize());
|
}
|
||||||
mi->assignUboAllocation(mUbHandle, newId, newOffset);
|
|
||||||
if (!BufferAllocator::isValid(newId)) {
|
// This instance doesn't need orphaning.
|
||||||
reallocationNeeded = true;
|
if (!mi->getUniformBuffer().isDirty() || !mAllocator.isLockedByGpu(mi->getAllocationId())) {
|
||||||
}
|
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() {
|
||||||
const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>&
|
for (auto* mi: mManagedInstances) {
|
||||||
materialInstances) {
|
auto [newId, newOffset] = mAllocator.allocate(mi->getUniformBuffer().getSize());
|
||||||
for (const auto& [_, miList] : materialInstances) {
|
assert_invariant(BufferAllocator::isValid(newId));
|
||||||
miList.forEach([this](FMaterialInstance* mi) {
|
mi->assignUboAllocation(mUbHandle, newId, newOffset);
|
||||||
if (!mi->isUsingUboBatching()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto [newId, newOffset] = mAllocator.allocate(mi->getUniformBuffer().getSize());
|
|
||||||
assert_invariant(BufferAllocator::isValid(newId));
|
|
||||||
mi->assignUboAllocation(mUbHandle, newId, newOffset);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,28 +288,19 @@ 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& materialInstance: materialInstances) {
|
for (const auto* mi: mManagedInstances) {
|
||||||
materialInstance.second.forEach([&newBufferSize, &allocator](const FMaterialInstance* mi) {
|
const AllocationId allocationId = mi->getAllocationId();
|
||||||
if (!mi->isUsingUboBatching()) {
|
if (allocationId == BufferAllocator::REALLOCATION_REQUIRED) {
|
||||||
return;
|
// 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 * mAllocator.alignUp(mi->getUniformBuffer().getSize());
|
||||||
const AllocationId allocationId = mi->getAllocationId();
|
} else {
|
||||||
if (allocationId == BufferAllocator::REALLOCATION_REQUIRED) {
|
newBufferSize += mAllocator.alignUp(mi->getUniformBuffer().getSize());
|
||||||
// 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 allocator.alignUp(newBufferSize * BUFFER_SIZE_GROWTH_MULTIPLIER);
|
return mAllocator.alignUp(newBufferSize * BUFFER_SIZE_GROWTH_MULTIPLIER);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace filament
|
} // namespace filament
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
#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"
|
||||||
@@ -29,6 +28,8 @@
|
|||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
class UboManagerTest;
|
||||||
|
|
||||||
namespace filament {
|
namespace filament {
|
||||||
|
|
||||||
class FMaterial;
|
class FMaterial;
|
||||||
@@ -96,9 +97,8 @@ 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.
|
||||||
void beginFrame(backend::DriverApi& driver,
|
// Note that it must happen before committing all MIs.
|
||||||
const std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>>&
|
void beginFrame(backend::DriverApi& driver);
|
||||||
materialInstances);
|
|
||||||
|
|
||||||
// Unmap the buffer here
|
// Unmap the buffer here
|
||||||
void finishBeginFrame(backend::DriverApi& driver);
|
void finishBeginFrame(backend::DriverApi& driver);
|
||||||
@@ -106,23 +106,31 @@ 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 when a material instance is no longer holding a slot. e.g. it is destroyed.
|
// Call this to register a new material instance to UboManager.
|
||||||
void retireSlot(BufferAllocator::AllocationId id);
|
void manageMaterialInstance(FMaterialInstance* instance);
|
||||||
|
|
||||||
|
// 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 {
|
||||||
@@ -134,23 +142,19 @@ 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,53 +285,45 @@ 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
|
|
||||||
for (size_t i = 0, n = mAttributes.size(); i < n; ++i) {
|
auto shouldCreateBuffer = [this](size_t attributeIndex) {
|
||||||
if (mDeclaredAttributes[i]) {
|
const uint8_t slot = mAttributes[attributeIndex].buffer;
|
||||||
const uint32_t offset = mAttributes[i].offset;
|
return mDeclaredAttributes[attributeIndex] && slot != Attribute::BUFFER_UNUSED &&
|
||||||
const uint8_t stride = mAttributes[i].stride;
|
!mBufferObjects[slot];
|
||||||
const uint8_t slot = mAttributes[i].buffer;
|
};
|
||||||
const size_t end = offset + mVertexCount * stride;
|
auto updateBufferSize = [&bufferSizes, this](size_t attributeIndex) {
|
||||||
if (slot != Attribute::BUFFER_UNUSED) {
|
const uint32_t offset = mAttributes[attributeIndex].offset;
|
||||||
assert_invariant(slot < MAX_VERTEX_BUFFER_COUNT);
|
const uint8_t stride = mAttributes[attributeIndex].stride;
|
||||||
bufferSizes[slot] = std::max(bufferSizes[slot], end);
|
const uint8_t slot = mAttributes[attributeIndex].buffer;
|
||||||
}
|
const size_t end = offset + mVertexCount * stride;
|
||||||
}
|
assert_invariant(slot < MAX_VERTEX_BUFFER_COUNT);
|
||||||
}
|
bufferSizes[slot] = std::max(bufferSizes[slot], end);
|
||||||
|
};
|
||||||
|
|
||||||
if (!mBufferObjectsEnabled) {
|
if (!mBufferObjectsEnabled) {
|
||||||
// If buffer objects are not enabled at the API level, then we create them internally.
|
|
||||||
#pragma nounroll
|
#pragma nounroll
|
||||||
for (size_t index = 0; index < MAX_VERTEX_ATTRIBUTE_COUNT; ++index) {
|
for (size_t i = 0, n = mAttributes.size(); i < n; ++i) {
|
||||||
size_t const i = mAttributes[index].buffer;
|
if (shouldCreateBuffer(i)) {
|
||||||
if (i != Attribute::BUFFER_UNUSED) {
|
updateBufferSize(i);
|
||||||
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 {
|
} else if (mAdvancedSkinningEnabled) {
|
||||||
// in advanced skinning mode, we manage the BONE_INDICES and BONE_WEIGHTS arrays ourselves,
|
// For advanced skinning mode, only relevant buffers (BONE_INDICES & BONE_WEIGHTS) are
|
||||||
// so we have to set the corresponding buffer objects.
|
// created. We manually populated the relevant attributes for those buffers above.
|
||||||
if (mAdvancedSkinningEnabled) {
|
updateBufferSize(BONE_INDICES);
|
||||||
for (auto const index : { BONE_INDICES, BONE_WEIGHTS }) {
|
updateBufferSize(BONE_WEIGHTS);
|
||||||
size_t const i = mAttributes[index].buffer;
|
}
|
||||||
assert_invariant(i != Attribute::BUFFER_UNUSED);
|
|
||||||
assert_invariant(bufferSizes[i] > 0);
|
// create buffers
|
||||||
if (!mBufferObjects[i]) {
|
for (size_t i = 0; i < MAX_VERTEX_BUFFER_COUNT; ++i) {
|
||||||
BufferObjectHandle const bo = driver.createBufferObject(bufferSizes[i],
|
if (bufferSizes[i] == 0 || mBufferObjects[i]) {
|
||||||
BufferObjectBinding::VERTEX, BufferUsage::STATIC,
|
continue;
|
||||||
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,14 +50,17 @@ 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)
|
||||||
|
|||||||
@@ -0,0 +1,366 @@
|
|||||||
|
/*
|
||||||
|
* 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.0"
|
spec.version = "1.67.1"
|
||||||
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.0/filament-v1.67.0-ios.tgz" }
|
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.67.1/filament-v1.67.1-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 = {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
namespace filament {
|
namespace filament {
|
||||||
|
|
||||||
// update this when a new version of filament wouldn't work with older materials
|
// update this when a new version of filament wouldn't work with older materials
|
||||||
static constexpr size_t MATERIAL_VERSION = 67;
|
static constexpr size_t MATERIAL_VERSION = 68;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Supported shading models
|
* Supported shading models
|
||||||
|
|||||||
@@ -190,6 +190,47 @@ void ImGuiHelper::render(float timeStepInSeconds, Callback imguiCommands) {
|
|||||||
void ImGuiHelper::processImGuiCommands(ImDrawData* commands, const ImGuiIO& io) {
|
void ImGuiHelper::processImGuiCommands(ImDrawData* commands, const ImGuiIO& io) {
|
||||||
ImGui::SetCurrentContext(mImGuiContext);
|
ImGui::SetCurrentContext(mImGuiContext);
|
||||||
|
|
||||||
|
if (commands->Textures != nullptr) {
|
||||||
|
for (ImTextureData* tex : *commands->Textures) {
|
||||||
|
if (tex->Status == ImTextureStatus_OK) {
|
||||||
|
continue;
|
||||||
|
} else if (tex->Status == ImTextureStatus_WantCreate) {
|
||||||
|
IM_ASSERT(tex->TexID == 0 && tex->BackendUserData == nullptr);
|
||||||
|
Texture* ftex = Texture::Builder()
|
||||||
|
.width(tex->Width)
|
||||||
|
.height(tex->Height)
|
||||||
|
.levels(1)
|
||||||
|
.format(Texture::InternalFormat::RGBA8)
|
||||||
|
.sampler(Texture::Sampler::SAMPLER_2D)
|
||||||
|
.build(*mEngine);
|
||||||
|
|
||||||
|
IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
|
||||||
|
const int size = tex->Width * tex->Height * 4;
|
||||||
|
Texture::PixelBufferDescriptor pb(tex->GetPixels(), size,
|
||||||
|
Texture::Format::RGBA,
|
||||||
|
Texture::Type::UBYTE);
|
||||||
|
ftex->setImage(*mEngine, 0, std::move(pb));
|
||||||
|
|
||||||
|
tex->SetTexID((ImTextureID)ftex);
|
||||||
|
tex->SetStatus(ImTextureStatus_OK);
|
||||||
|
} else if (tex->Status == ImTextureStatus_WantUpdates) {
|
||||||
|
const int size = tex->Width * tex->Height * 4;
|
||||||
|
Texture::PixelBufferDescriptor pb(tex->GetPixels(), size,
|
||||||
|
Texture::Format::RGBA,
|
||||||
|
Texture::Type::UBYTE);
|
||||||
|
filament::Texture* ftex = (filament::Texture*)tex->TexID;
|
||||||
|
ftex->setImage(*mEngine, 0, std::move(pb));
|
||||||
|
tex->SetStatus(ImTextureStatus_OK);
|
||||||
|
} else if (tex->Status == ImTextureStatus_WantDestroy &&
|
||||||
|
tex->UnusedFrames > 0) {
|
||||||
|
filament::Texture* ftex = (filament::Texture*)tex->TexID;
|
||||||
|
mEngine->destroy(ftex);
|
||||||
|
tex->SetTexID(ImTextureID_Invalid);
|
||||||
|
tex->SetStatus(ImTextureStatus_Destroyed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mHasSynced = false;
|
mHasSynced = false;
|
||||||
auto& rcm = mEngine->getRenderableManager();
|
auto& rcm = mEngine->getRenderableManager();
|
||||||
|
|
||||||
@@ -228,7 +269,7 @@ void ImGuiHelper::processImGuiCommands(ImDrawData* commands, const ImGuiIO& io)
|
|||||||
if (pcmd.UserCallback) {
|
if (pcmd.UserCallback) {
|
||||||
pcmd.UserCallback(cmds, &pcmd);
|
pcmd.UserCallback(cmds, &pcmd);
|
||||||
} else {
|
} else {
|
||||||
auto texture = (Texture const*)pcmd.TextureId;
|
auto texture = (Texture const*)pcmd.GetTexID();
|
||||||
MaterialInstance* materialInstance;
|
MaterialInstance* materialInstance;
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
if (texture && texture->getTarget() == Texture::Sampler::SAMPLER_EXTERNAL) {
|
if (texture && texture->getTarget() == Texture::Sampler::SAMPLER_EXTERNAL) {
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ set(SRCS
|
|||||||
src/MeshAssimp.cpp
|
src/MeshAssimp.cpp
|
||||||
src/Sphere.cpp
|
src/Sphere.cpp
|
||||||
src/VulkanPlatformHelper.cpp
|
src/VulkanPlatformHelper.cpp
|
||||||
|
src/KeyInputConversion.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(LIBS
|
set(LIBS
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
#include <filamentapp/FilamentApp.h>
|
#include <filamentapp/FilamentApp.h>
|
||||||
|
|
||||||
|
#include "KeyInputConversion.h"
|
||||||
|
|
||||||
#if defined(WIN32)
|
#if defined(WIN32)
|
||||||
# include <SDL_syswm.h>
|
# include <SDL_syswm.h>
|
||||||
# include <utils/unwindows.h>
|
# include <utils/unwindows.h>
|
||||||
@@ -211,27 +213,6 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
|
|||||||
SDL_GetWindowWMInfo(window->getSDLWindow(), &wmInfo);
|
SDL_GetWindowWMInfo(window->getSDLWindow(), &wmInfo);
|
||||||
ImGui::GetMainViewport()->PlatformHandleRaw = wmInfo.info.win.window;
|
ImGui::GetMainViewport()->PlatformHandleRaw = wmInfo.info.win.window;
|
||||||
#endif
|
#endif
|
||||||
io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB;
|
|
||||||
io.KeyMap[ImGuiKey_LeftArrow] = SDL_SCANCODE_LEFT;
|
|
||||||
io.KeyMap[ImGuiKey_RightArrow] = SDL_SCANCODE_RIGHT;
|
|
||||||
io.KeyMap[ImGuiKey_UpArrow] = SDL_SCANCODE_UP;
|
|
||||||
io.KeyMap[ImGuiKey_DownArrow] = SDL_SCANCODE_DOWN;
|
|
||||||
io.KeyMap[ImGuiKey_PageUp] = SDL_SCANCODE_PAGEUP;
|
|
||||||
io.KeyMap[ImGuiKey_PageDown] = SDL_SCANCODE_PAGEDOWN;
|
|
||||||
io.KeyMap[ImGuiKey_Home] = SDL_SCANCODE_HOME;
|
|
||||||
io.KeyMap[ImGuiKey_End] = SDL_SCANCODE_END;
|
|
||||||
io.KeyMap[ImGuiKey_Insert] = SDL_SCANCODE_INSERT;
|
|
||||||
io.KeyMap[ImGuiKey_Delete] = SDL_SCANCODE_DELETE;
|
|
||||||
io.KeyMap[ImGuiKey_Backspace] = SDL_SCANCODE_BACKSPACE;
|
|
||||||
io.KeyMap[ImGuiKey_Space] = SDL_SCANCODE_SPACE;
|
|
||||||
io.KeyMap[ImGuiKey_Enter] = SDL_SCANCODE_RETURN;
|
|
||||||
io.KeyMap[ImGuiKey_Escape] = SDL_SCANCODE_ESCAPE;
|
|
||||||
io.KeyMap[ImGuiKey_A] = SDL_SCANCODE_A;
|
|
||||||
io.KeyMap[ImGuiKey_C] = SDL_SCANCODE_C;
|
|
||||||
io.KeyMap[ImGuiKey_V] = SDL_SCANCODE_V;
|
|
||||||
io.KeyMap[ImGuiKey_X] = SDL_SCANCODE_X;
|
|
||||||
io.KeyMap[ImGuiKey_Y] = SDL_SCANCODE_Y;
|
|
||||||
io.KeyMap[ImGuiKey_Z] = SDL_SCANCODE_Z;
|
|
||||||
io.SetClipboardTextFn = [](void*, const char* text) {
|
io.SetClipboardTextFn = [](void*, const char* text) {
|
||||||
SDL_SetClipboardText(text);
|
SDL_SetClipboardText(text);
|
||||||
};
|
};
|
||||||
@@ -305,15 +286,19 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
|
|||||||
io.AddInputCharactersUTF8(event->text.text);
|
io.AddInputCharactersUTF8(event->text.text);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDL_KEYDOWN:
|
case SDL_KEYUP:
|
||||||
case SDL_KEYUP: {
|
case SDL_KEYDOWN: {
|
||||||
int key = event->key.keysym.scancode;
|
SDL_Scancode const scancode = event->key.keysym.scancode;
|
||||||
IM_ASSERT(key >= 0 && key < IM_ARRAYSIZE(io.KeysDown));
|
SDL_Keycode const keycode = event->key.keysym.sym;
|
||||||
io.KeysDown[key] = (event->type == SDL_KEYDOWN);
|
|
||||||
io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0);
|
io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0);
|
||||||
io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0);
|
io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0);
|
||||||
io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0);
|
io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0);
|
||||||
io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0);
|
io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0);
|
||||||
|
|
||||||
|
utils::slog.e <<"control=" << io.KeyCtrl << utils::io::endl;
|
||||||
|
io.AddKeyEvent(
|
||||||
|
filamentapp_utils::ImGui_ImplSDL2_KeyEventToImGuiKey(keycode, scancode),
|
||||||
|
event->type == SDL_KEYDOWN);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,297 @@
|
|||||||
|
/*
|
||||||
|
* 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_FILAMENTAPP_KEYINPUTCONVERSION_H
|
||||||
|
#define TNT_FILAMENT_FILAMENTAPP_KEYINPUTCONVERSION_H
|
||||||
|
|
||||||
|
#include <imgui.h>
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
|
namespace filamentapp_utils {
|
||||||
|
|
||||||
|
// Copied from
|
||||||
|
// https://github.com/ocornut/imgui/blob/940627d008b8f0584b7f50d24574537cf24a32e1/backends/imgui_impl_sdl2.cpp#L206
|
||||||
|
ImGuiKey ImGui_ImplSDL2_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode) {
|
||||||
|
switch (keycode) {
|
||||||
|
case SDLK_TAB:
|
||||||
|
return ImGuiKey_Tab;
|
||||||
|
case SDLK_LEFT:
|
||||||
|
return ImGuiKey_LeftArrow;
|
||||||
|
case SDLK_RIGHT:
|
||||||
|
return ImGuiKey_RightArrow;
|
||||||
|
case SDLK_UP:
|
||||||
|
return ImGuiKey_UpArrow;
|
||||||
|
case SDLK_DOWN:
|
||||||
|
return ImGuiKey_DownArrow;
|
||||||
|
case SDLK_PAGEUP:
|
||||||
|
return ImGuiKey_PageUp;
|
||||||
|
case SDLK_PAGEDOWN:
|
||||||
|
return ImGuiKey_PageDown;
|
||||||
|
case SDLK_HOME:
|
||||||
|
return ImGuiKey_Home;
|
||||||
|
case SDLK_END:
|
||||||
|
return ImGuiKey_End;
|
||||||
|
case SDLK_INSERT:
|
||||||
|
return ImGuiKey_Insert;
|
||||||
|
case SDLK_DELETE:
|
||||||
|
return ImGuiKey_Delete;
|
||||||
|
case SDLK_BACKSPACE:
|
||||||
|
return ImGuiKey_Backspace;
|
||||||
|
case SDLK_SPACE:
|
||||||
|
return ImGuiKey_Space;
|
||||||
|
case SDLK_RETURN:
|
||||||
|
return ImGuiKey_Enter;
|
||||||
|
case SDLK_ESCAPE:
|
||||||
|
return ImGuiKey_Escape;
|
||||||
|
// case SDLK_QUOTE: return ImGuiKey_Apostrophe;
|
||||||
|
case SDLK_COMMA:
|
||||||
|
return ImGuiKey_Comma;
|
||||||
|
// case SDLK_MINUS: return ImGuiKey_Minus;
|
||||||
|
case SDLK_PERIOD:
|
||||||
|
return ImGuiKey_Period;
|
||||||
|
// case SDLK_SLASH: return ImGuiKey_Slash;
|
||||||
|
case SDLK_SEMICOLON:
|
||||||
|
return ImGuiKey_Semicolon;
|
||||||
|
// case SDLK_EQUALS: return ImGuiKey_Equal;
|
||||||
|
// case SDLK_LEFTBRACKET: return ImGuiKey_LeftBracket;
|
||||||
|
// case SDLK_BACKSLASH: return ImGuiKey_Backslash;
|
||||||
|
// case SDLK_RIGHTBRACKET: return ImGuiKey_RightBracket;
|
||||||
|
// case SDLK_BACKQUOTE: return ImGuiKey_GraveAccent;
|
||||||
|
case SDLK_CAPSLOCK:
|
||||||
|
return ImGuiKey_CapsLock;
|
||||||
|
case SDLK_SCROLLLOCK:
|
||||||
|
return ImGuiKey_ScrollLock;
|
||||||
|
case SDLK_NUMLOCKCLEAR:
|
||||||
|
return ImGuiKey_NumLock;
|
||||||
|
case SDLK_PRINTSCREEN:
|
||||||
|
return ImGuiKey_PrintScreen;
|
||||||
|
case SDLK_PAUSE:
|
||||||
|
return ImGuiKey_Pause;
|
||||||
|
case SDLK_KP_0:
|
||||||
|
return ImGuiKey_Keypad0;
|
||||||
|
case SDLK_KP_1:
|
||||||
|
return ImGuiKey_Keypad1;
|
||||||
|
case SDLK_KP_2:
|
||||||
|
return ImGuiKey_Keypad2;
|
||||||
|
case SDLK_KP_3:
|
||||||
|
return ImGuiKey_Keypad3;
|
||||||
|
case SDLK_KP_4:
|
||||||
|
return ImGuiKey_Keypad4;
|
||||||
|
case SDLK_KP_5:
|
||||||
|
return ImGuiKey_Keypad5;
|
||||||
|
case SDLK_KP_6:
|
||||||
|
return ImGuiKey_Keypad6;
|
||||||
|
case SDLK_KP_7:
|
||||||
|
return ImGuiKey_Keypad7;
|
||||||
|
case SDLK_KP_8:
|
||||||
|
return ImGuiKey_Keypad8;
|
||||||
|
case SDLK_KP_9:
|
||||||
|
return ImGuiKey_Keypad9;
|
||||||
|
case SDLK_KP_PERIOD:
|
||||||
|
return ImGuiKey_KeypadDecimal;
|
||||||
|
case SDLK_KP_DIVIDE:
|
||||||
|
return ImGuiKey_KeypadDivide;
|
||||||
|
case SDLK_KP_MULTIPLY:
|
||||||
|
return ImGuiKey_KeypadMultiply;
|
||||||
|
case SDLK_KP_MINUS:
|
||||||
|
return ImGuiKey_KeypadSubtract;
|
||||||
|
case SDLK_KP_PLUS:
|
||||||
|
return ImGuiKey_KeypadAdd;
|
||||||
|
case SDLK_KP_ENTER:
|
||||||
|
return ImGuiKey_KeypadEnter;
|
||||||
|
case SDLK_KP_EQUALS:
|
||||||
|
return ImGuiKey_KeypadEqual;
|
||||||
|
case SDLK_LCTRL:
|
||||||
|
return ImGuiKey_LeftCtrl;
|
||||||
|
case SDLK_LSHIFT:
|
||||||
|
return ImGuiKey_LeftShift;
|
||||||
|
case SDLK_LALT:
|
||||||
|
return ImGuiKey_LeftAlt;
|
||||||
|
case SDLK_LGUI:
|
||||||
|
return ImGuiKey_LeftSuper;
|
||||||
|
case SDLK_RCTRL:
|
||||||
|
return ImGuiKey_RightCtrl;
|
||||||
|
case SDLK_RSHIFT:
|
||||||
|
return ImGuiKey_RightShift;
|
||||||
|
case SDLK_RALT:
|
||||||
|
return ImGuiKey_RightAlt;
|
||||||
|
case SDLK_RGUI:
|
||||||
|
return ImGuiKey_RightSuper;
|
||||||
|
case SDLK_APPLICATION:
|
||||||
|
return ImGuiKey_Menu;
|
||||||
|
case SDLK_0:
|
||||||
|
return ImGuiKey_0;
|
||||||
|
case SDLK_1:
|
||||||
|
return ImGuiKey_1;
|
||||||
|
case SDLK_2:
|
||||||
|
return ImGuiKey_2;
|
||||||
|
case SDLK_3:
|
||||||
|
return ImGuiKey_3;
|
||||||
|
case SDLK_4:
|
||||||
|
return ImGuiKey_4;
|
||||||
|
case SDLK_5:
|
||||||
|
return ImGuiKey_5;
|
||||||
|
case SDLK_6:
|
||||||
|
return ImGuiKey_6;
|
||||||
|
case SDLK_7:
|
||||||
|
return ImGuiKey_7;
|
||||||
|
case SDLK_8:
|
||||||
|
return ImGuiKey_8;
|
||||||
|
case SDLK_9:
|
||||||
|
return ImGuiKey_9;
|
||||||
|
case SDLK_a:
|
||||||
|
return ImGuiKey_A;
|
||||||
|
case SDLK_b:
|
||||||
|
return ImGuiKey_B;
|
||||||
|
case SDLK_c:
|
||||||
|
return ImGuiKey_C;
|
||||||
|
case SDLK_d:
|
||||||
|
return ImGuiKey_D;
|
||||||
|
case SDLK_e:
|
||||||
|
return ImGuiKey_E;
|
||||||
|
case SDLK_f:
|
||||||
|
return ImGuiKey_F;
|
||||||
|
case SDLK_g:
|
||||||
|
return ImGuiKey_G;
|
||||||
|
case SDLK_h:
|
||||||
|
return ImGuiKey_H;
|
||||||
|
case SDLK_i:
|
||||||
|
return ImGuiKey_I;
|
||||||
|
case SDLK_j:
|
||||||
|
return ImGuiKey_J;
|
||||||
|
case SDLK_k:
|
||||||
|
return ImGuiKey_K;
|
||||||
|
case SDLK_l:
|
||||||
|
return ImGuiKey_L;
|
||||||
|
case SDLK_m:
|
||||||
|
return ImGuiKey_M;
|
||||||
|
case SDLK_n:
|
||||||
|
return ImGuiKey_N;
|
||||||
|
case SDLK_o:
|
||||||
|
return ImGuiKey_O;
|
||||||
|
case SDLK_p:
|
||||||
|
return ImGuiKey_P;
|
||||||
|
case SDLK_q:
|
||||||
|
return ImGuiKey_Q;
|
||||||
|
case SDLK_r:
|
||||||
|
return ImGuiKey_R;
|
||||||
|
case SDLK_s:
|
||||||
|
return ImGuiKey_S;
|
||||||
|
case SDLK_t:
|
||||||
|
return ImGuiKey_T;
|
||||||
|
case SDLK_u:
|
||||||
|
return ImGuiKey_U;
|
||||||
|
case SDLK_v:
|
||||||
|
return ImGuiKey_V;
|
||||||
|
case SDLK_w:
|
||||||
|
return ImGuiKey_W;
|
||||||
|
case SDLK_x:
|
||||||
|
return ImGuiKey_X;
|
||||||
|
case SDLK_y:
|
||||||
|
return ImGuiKey_Y;
|
||||||
|
case SDLK_z:
|
||||||
|
return ImGuiKey_Z;
|
||||||
|
case SDLK_F1:
|
||||||
|
return ImGuiKey_F1;
|
||||||
|
case SDLK_F2:
|
||||||
|
return ImGuiKey_F2;
|
||||||
|
case SDLK_F3:
|
||||||
|
return ImGuiKey_F3;
|
||||||
|
case SDLK_F4:
|
||||||
|
return ImGuiKey_F4;
|
||||||
|
case SDLK_F5:
|
||||||
|
return ImGuiKey_F5;
|
||||||
|
case SDLK_F6:
|
||||||
|
return ImGuiKey_F6;
|
||||||
|
case SDLK_F7:
|
||||||
|
return ImGuiKey_F7;
|
||||||
|
case SDLK_F8:
|
||||||
|
return ImGuiKey_F8;
|
||||||
|
case SDLK_F9:
|
||||||
|
return ImGuiKey_F9;
|
||||||
|
case SDLK_F10:
|
||||||
|
return ImGuiKey_F10;
|
||||||
|
case SDLK_F11:
|
||||||
|
return ImGuiKey_F11;
|
||||||
|
case SDLK_F12:
|
||||||
|
return ImGuiKey_F12;
|
||||||
|
case SDLK_F13:
|
||||||
|
return ImGuiKey_F13;
|
||||||
|
case SDLK_F14:
|
||||||
|
return ImGuiKey_F14;
|
||||||
|
case SDLK_F15:
|
||||||
|
return ImGuiKey_F15;
|
||||||
|
case SDLK_F16:
|
||||||
|
return ImGuiKey_F16;
|
||||||
|
case SDLK_F17:
|
||||||
|
return ImGuiKey_F17;
|
||||||
|
case SDLK_F18:
|
||||||
|
return ImGuiKey_F18;
|
||||||
|
case SDLK_F19:
|
||||||
|
return ImGuiKey_F19;
|
||||||
|
case SDLK_F20:
|
||||||
|
return ImGuiKey_F20;
|
||||||
|
case SDLK_F21:
|
||||||
|
return ImGuiKey_F21;
|
||||||
|
case SDLK_F22:
|
||||||
|
return ImGuiKey_F22;
|
||||||
|
case SDLK_F23:
|
||||||
|
return ImGuiKey_F23;
|
||||||
|
case SDLK_F24:
|
||||||
|
return ImGuiKey_F24;
|
||||||
|
case SDLK_AC_BACK:
|
||||||
|
return ImGuiKey_AppBack;
|
||||||
|
case SDLK_AC_FORWARD:
|
||||||
|
return ImGuiKey_AppForward;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to scancode
|
||||||
|
switch (scancode) {
|
||||||
|
case SDL_SCANCODE_GRAVE:
|
||||||
|
return ImGuiKey_GraveAccent;
|
||||||
|
case SDL_SCANCODE_MINUS:
|
||||||
|
return ImGuiKey_Minus;
|
||||||
|
case SDL_SCANCODE_EQUALS:
|
||||||
|
return ImGuiKey_Equal;
|
||||||
|
case SDL_SCANCODE_LEFTBRACKET:
|
||||||
|
return ImGuiKey_LeftBracket;
|
||||||
|
case SDL_SCANCODE_RIGHTBRACKET:
|
||||||
|
return ImGuiKey_RightBracket;
|
||||||
|
case SDL_SCANCODE_NONUSBACKSLASH:
|
||||||
|
return ImGuiKey_Oem102;
|
||||||
|
case SDL_SCANCODE_BACKSLASH:
|
||||||
|
return ImGuiKey_Backslash;
|
||||||
|
case SDL_SCANCODE_SEMICOLON:
|
||||||
|
return ImGuiKey_Semicolon;
|
||||||
|
case SDL_SCANCODE_APOSTROPHE:
|
||||||
|
return ImGuiKey_Apostrophe;
|
||||||
|
case SDL_SCANCODE_COMMA:
|
||||||
|
return ImGuiKey_Comma;
|
||||||
|
case SDL_SCANCODE_PERIOD:
|
||||||
|
return ImGuiKey_Period;
|
||||||
|
case SDL_SCANCODE_SLASH:
|
||||||
|
return ImGuiKey_Slash;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ImGuiKey_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace filamentapp_utils
|
||||||
|
|
||||||
|
#endif // TNT_FILAMENT_FILAMENTAPP_KEYINPUTCONVERSION_H
|
||||||
@@ -56,6 +56,9 @@ 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.
|
||||||
@@ -65,7 +68,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 (full()) {
|
if (UTILS_LIKELY(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;
|
||||||
|
|||||||
@@ -41,6 +41,36 @@ using namespace filament::math;
|
|||||||
namespace filament {
|
namespace filament {
|
||||||
namespace viewer {
|
namespace viewer {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
ImGuiKey convertKey(int keycode) {
|
||||||
|
|
||||||
|
// The following table uses normal ANSI codes, which is consistent with the keyCode that
|
||||||
|
// comes from a web "keydown" event.
|
||||||
|
switch (keycode) {
|
||||||
|
case 9: return ImGuiKey_Tab;
|
||||||
|
case 37: return ImGuiKey_LeftArrow;
|
||||||
|
case 39: return ImGuiKey_RightArrow;
|
||||||
|
case 38: return ImGuiKey_UpArrow;
|
||||||
|
case 40: return ImGuiKey_DownArrow;
|
||||||
|
case 36: return ImGuiKey_Home;
|
||||||
|
case 35: return ImGuiKey_End;
|
||||||
|
case 46: return ImGuiKey_Delete;
|
||||||
|
case 8: return ImGuiKey_Backspace;
|
||||||
|
case 13: return ImGuiKey_Enter;
|
||||||
|
case 27: return ImGuiKey_Escape;
|
||||||
|
case 65: return ImGuiKey_A;
|
||||||
|
case 67: return ImGuiKey_C;
|
||||||
|
case 86: return ImGuiKey_V;
|
||||||
|
case 88: return ImGuiKey_X;
|
||||||
|
case 89: return ImGuiKey_Y;
|
||||||
|
case 90: return ImGuiKey_Z;
|
||||||
|
default: return ImGuiKey_None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
mat4f fitIntoUnitCube(const Aabb& bounds, float zoffset) {
|
mat4f fitIntoUnitCube(const Aabb& bounds, float zoffset) {
|
||||||
float3 minpt = bounds.min;
|
float3 minpt = bounds.min;
|
||||||
float3 maxpt = bounds.max;
|
float3 maxpt = bounds.max;
|
||||||
@@ -563,27 +593,6 @@ void ViewerGui::renderUserInterface(float timeStepInSeconds, View* guiView, floa
|
|||||||
mImGuiHelper = new ImGuiHelper(mEngine, guiView, "");
|
mImGuiHelper = new ImGuiHelper(mEngine, guiView, "");
|
||||||
|
|
||||||
auto& io = ImGui::GetIO();
|
auto& io = ImGui::GetIO();
|
||||||
|
|
||||||
// The following table uses normal ANSI codes, which is consistent with the keyCode that
|
|
||||||
// comes from a web "keydown" event.
|
|
||||||
io.KeyMap[ImGuiKey_Tab] = 9;
|
|
||||||
io.KeyMap[ImGuiKey_LeftArrow] = 37;
|
|
||||||
io.KeyMap[ImGuiKey_RightArrow] = 39;
|
|
||||||
io.KeyMap[ImGuiKey_UpArrow] = 38;
|
|
||||||
io.KeyMap[ImGuiKey_DownArrow] = 40;
|
|
||||||
io.KeyMap[ImGuiKey_Home] = 36;
|
|
||||||
io.KeyMap[ImGuiKey_End] = 35;
|
|
||||||
io.KeyMap[ImGuiKey_Delete] = 46;
|
|
||||||
io.KeyMap[ImGuiKey_Backspace] = 8;
|
|
||||||
io.KeyMap[ImGuiKey_Enter] = 13;
|
|
||||||
io.KeyMap[ImGuiKey_Escape] = 27;
|
|
||||||
io.KeyMap[ImGuiKey_A] = 65;
|
|
||||||
io.KeyMap[ImGuiKey_C] = 67;
|
|
||||||
io.KeyMap[ImGuiKey_V] = 86;
|
|
||||||
io.KeyMap[ImGuiKey_X] = 88;
|
|
||||||
io.KeyMap[ImGuiKey_Y] = 89;
|
|
||||||
io.KeyMap[ImGuiKey_Z] = 90;
|
|
||||||
|
|
||||||
// TODO: this is not the best way to handle high DPI in ImGui, but it is fine when using the
|
// TODO: this is not the best way to handle high DPI in ImGui, but it is fine when using the
|
||||||
// proggy font. Users need to refresh their window when dragging between displays with
|
// proggy font. Users need to refresh their window when dragging between displays with
|
||||||
// different pixel ratios.
|
// different pixel ratios.
|
||||||
@@ -614,15 +623,23 @@ void ViewerGui::mouseEvent(float mouseX, float mouseY, bool mouseButton, float m
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ViewerGui::keyDownEvent(int keyCode) {
|
void ViewerGui::keyDownEvent(int keyCode) {
|
||||||
if (mImGuiHelper && keyCode < IM_ARRAYSIZE(ImGui::GetIO().KeysDown)) {
|
if (!mImGuiHelper) {
|
||||||
ImGui::GetIO().KeysDown[keyCode] = true;
|
return;
|
||||||
}
|
}
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.AddKeyEvent(
|
||||||
|
convertKey(keyCode),
|
||||||
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewerGui::keyUpEvent(int keyCode) {
|
void ViewerGui::keyUpEvent(int keyCode) {
|
||||||
if (mImGuiHelper && keyCode < IM_ARRAYSIZE(ImGui::GetIO().KeysDown)) {
|
if (!mImGuiHelper) {
|
||||||
ImGui::GetIO().KeysDown[keyCode] = false;
|
return;
|
||||||
}
|
}
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.AddKeyEvent(
|
||||||
|
convertKey(keyCode),
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewerGui::keyPressEvent(int charCode) {
|
void ViewerGui::keyPressEvent(int charCode) {
|
||||||
|
|||||||
@@ -302,14 +302,16 @@ vec4 evaluateLights(const MaterialInputs material) {
|
|||||||
return vec4(color, computeDiffuseAlpha(material.baseColor.a));
|
return vec4(color, computeDiffuseAlpha(material.baseColor.a));
|
||||||
}
|
}
|
||||||
|
|
||||||
void addEmissive(const MaterialInputs material, inout vec4 color) {
|
vec3 emissive(const MaterialInputs material, float alpha) {
|
||||||
#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 *= color.a;
|
attenuation *= alpha;
|
||||||
#endif
|
#endif
|
||||||
color.rgb += emissive.rgb * attenuation;
|
return emissive.rgb * attenuation;
|
||||||
|
#else
|
||||||
|
return vec3(0.0);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,7 +323,7 @@ void addEmissive(const MaterialInputs material, inout vec4 color) {
|
|||||||
*/
|
*/
|
||||||
vec4 evaluateMaterial(const MaterialInputs material) {
|
vec4 evaluateMaterial(const MaterialInputs material) {
|
||||||
vec4 color = evaluateLights(material);
|
vec4 color = evaluateLights(material);
|
||||||
addEmissive(material, color);
|
color.rgb += emissive(material, color.a);
|
||||||
// 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
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
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
@@ -0,0 +1 @@
|
|||||||
|
.*third_party/.*
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
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.
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
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
@@ -0,0 +1,12 @@
|
|||||||
|
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
@@ -0,0 +1,35 @@
|
|||||||
|
#!/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 ..
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
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/...
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
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 .
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
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
|
||||||
|
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
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,8 +8,10 @@
|
|||||||
!/cmake/*.cmake
|
!/cmake/*.cmake
|
||||||
!/test/AssemblyTests.cmake
|
!/test/AssemblyTests.cmake
|
||||||
*~
|
*~
|
||||||
|
*.swp
|
||||||
*.pyc
|
*.pyc
|
||||||
__pycache__
|
__pycache__
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
# lcov
|
# lcov
|
||||||
*.lcov
|
*.lcov
|
||||||
@@ -44,6 +46,7 @@ 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/
|
||||||
@@ -56,3 +59,10 @@ 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
@@ -0,0 +1,18 @@
|
|||||||
|
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
@@ -1,28 +0,0 @@
|
|||||||
#!/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
@@ -1,199 +0,0 @@
|
|||||||
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
|
|
||||||
+85
-80
@@ -1,25 +1,30 @@
|
|||||||
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', 'c++',
|
"-x",
|
||||||
'-I', 'include',
|
"c++",
|
||||||
'-isystem', '/usr/include',
|
"-I",
|
||||||
'-isystem', '/usr/local/include',
|
"include",
|
||||||
|
"-isystem",
|
||||||
|
"/usr/include",
|
||||||
|
"-isystem",
|
||||||
|
"/usr/local/include",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -29,87 +34,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 entries
|
# The compilation_commands.json file generated by CMake does not have
|
||||||
# for header files. So we do our best by asking the db for flags for a
|
# entries for header files. So we do our best by asking the db for flags for
|
||||||
# corresponding source file, if any. If one exists, the flags for that file
|
# a corresponding source file, if any. If one exists, the flags for that
|
||||||
# should be good enough.
|
# file 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_:
|
)
|
||||||
return compilation_info
|
if compilation_info.compiler_flags_:
|
||||||
return None
|
return compilation_info
|
||||||
return database.GetCompilationInfoForFile( filename )
|
return None
|
||||||
|
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:
|
)
|
||||||
relative_to = DirectoryOfThisScript()
|
else:
|
||||||
final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to )
|
relative_to = DirectoryOfThisScript()
|
||||||
|
final_flags = MakeRelativePathsInFlagsAbsolute(flags, relative_to)
|
||||||
|
|
||||||
return {
|
return {"flags": final_flags, "do_cache": True}
|
||||||
'flags': final_flags,
|
|
||||||
'do_cache': True
|
|
||||||
}
|
|
||||||
|
|||||||
Vendored
+24
@@ -9,40 +9,64 @@
|
|||||||
# 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
+66
-6
@@ -1,9 +1,38 @@
|
|||||||
|
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",
|
||||||
values = {
|
constraint_values = ["@platforms//os:windows"],
|
||||||
"cpu": "x64_windows",
|
visibility = [":__subpackages__"],
|
||||||
|
)
|
||||||
|
|
||||||
|
config_setting(
|
||||||
|
name = "perfcounters",
|
||||||
|
define_values = {
|
||||||
|
"pfm": "1",
|
||||||
},
|
},
|
||||||
visibility = [":__subpackages__"],
|
visibility = [":__subpackages__"],
|
||||||
)
|
)
|
||||||
@@ -17,20 +46,51 @@ cc_library(
|
|||||||
],
|
],
|
||||||
exclude = ["src/benchmark_main.cc"],
|
exclude = ["src/benchmark_main.cc"],
|
||||||
),
|
),
|
||||||
hdrs = ["include/benchmark/benchmark.h"],
|
hdrs = [
|
||||||
|
"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"],
|
||||||
}),
|
}),
|
||||||
strip_include_prefix = "include",
|
# Only static linking is allowed; no .so will be produced.
|
||||||
|
# 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 = ["include/benchmark/benchmark.h"],
|
hdrs = [
|
||||||
strip_include_prefix = "include",
|
"include/benchmark/benchmark.h",
|
||||||
|
"include/benchmark/export.h",
|
||||||
|
],
|
||||||
|
includes = ["include"],
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [":benchmark"],
|
deps = [":benchmark"],
|
||||||
)
|
)
|
||||||
|
|||||||
Vendored
+146
-61
@@ -1,27 +1,34 @@
|
|||||||
cmake_minimum_required (VERSION 2.8.12)
|
# Require CMake 3.10. If available, use the policies up to CMake 3.22.
|
||||||
|
cmake_minimum_required (VERSION 3.13...3.22)
|
||||||
|
|
||||||
project (benchmark)
|
project (benchmark VERSION 1.9.4 LANGUAGES CXX)
|
||||||
|
|
||||||
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)
|
||||||
if(NOT MSVC)
|
option(BENCHMARK_ENABLE_WERROR "Build Release candidates with -Werror." ON)
|
||||||
|
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.
|
||||||
@@ -30,6 +37,24 @@ 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)
|
||||||
@@ -41,7 +66,7 @@ function(should_enable_assembly_tests)
|
|||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
if (MSVC)
|
if (MSVC OR CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC")
|
||||||
return()
|
return()
|
||||||
elseif(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
|
elseif(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
|
||||||
return()
|
return()
|
||||||
@@ -77,29 +102,63 @@ 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
|
||||||
string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" VERSION ${GIT_VERSION})
|
message(STATUS "Google Benchmark version: ${VERSION}, normalized to ${NORMALIZED_VERSION}")
|
||||||
message(STATUS "Version: ${VERSION}")
|
|
||||||
|
|
||||||
# The version of the libraries
|
# The version of the libraries
|
||||||
set(GENERIC_LIB_VERSION ${VERSION})
|
set(GENERIC_LIB_VERSION ${NORMALIZED_VERSION})
|
||||||
string(SUBSTRING ${VERSION} 0 1 GENERIC_LIB_SOVERSION)
|
string(SUBSTRING ${NORMALIZED_VERSION} 0 1 GENERIC_LIB_SOVERSION)
|
||||||
|
|
||||||
# Import our CMake modules
|
# Import our CMake modules
|
||||||
include(CheckCXXCompilerFlag)
|
|
||||||
include(AddCXXCompilerFlag)
|
include(AddCXXCompilerFlag)
|
||||||
|
include(CheckCXXCompilerFlag)
|
||||||
|
include(CheckLibraryExists)
|
||||||
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-)
|
||||||
@@ -126,45 +185,48 @@ 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()
|
||||||
# Try and enable C++11. Don't use C++14 because it doesn't work in some
|
# Turn on Large-file Support
|
||||||
# configurations.
|
add_definitions(-D_FILE_OFFSET_BITS=64)
|
||||||
add_cxx_compiler_flag(-std=c++11)
|
add_definitions(-D_LARGEFILE64_SOURCE)
|
||||||
if (NOT HAVE_CXX_FLAG_STD_CXX11)
|
add_definitions(-D_LARGEFILE_SOURCE)
|
||||||
add_cxx_compiler_flag(-std=c++0x)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Turn compiler warnings up to 11
|
# Turn compiler warnings up to 11
|
||||||
if (NOT MSVC)
|
add_cxx_compiler_flag(-Wall)
|
||||||
add_cxx_compiler_flag(-Wall)
|
add_cxx_compiler_flag(-Wextra)
|
||||||
add_cxx_compiler_flag(-Wextra)
|
add_cxx_compiler_flag(-Wshadow)
|
||||||
add_cxx_compiler_flag(-Wshadow)
|
add_cxx_compiler_flag(-Wfloat-equal)
|
||||||
add_cxx_compiler_flag(-Werror RELEASE)
|
add_cxx_compiler_flag(-Wold-style-cast)
|
||||||
add_cxx_compiler_flag(-Werror RELWITHDEBINFO)
|
add_cxx_compiler_flag(-Wconversion)
|
||||||
add_cxx_compiler_flag(-Werror MINSIZEREL)
|
if(BENCHMARK_ENABLE_WERROR)
|
||||||
add_cxx_compiler_flag(-pedantic)
|
add_cxx_compiler_flag(-Werror)
|
||||||
add_cxx_compiler_flag(-pedantic-errors)
|
endif()
|
||||||
add_cxx_compiler_flag(-Wshorten-64-to-32)
|
if (NOT BENCHMARK_ENABLE_TESTING)
|
||||||
add_cxx_compiler_flag(-fstrict-aliasing)
|
# Disable warning when compiling tests as gtest does not use 'override'.
|
||||||
# Disable warnings regarding deprecated parts of the library while building
|
add_cxx_compiler_flag(-Wsuggest-override)
|
||||||
# and testing those parts of the library.
|
endif()
|
||||||
add_cxx_compiler_flag(-Wno-deprecated-declarations)
|
add_cxx_compiler_flag(-pedantic)
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
|
add_cxx_compiler_flag(-pedantic-errors)
|
||||||
# Intel silently ignores '-Wno-deprecated-declarations',
|
add_cxx_compiler_flag(-Wshorten-64-to-32)
|
||||||
# warning no. 1786 must be explicitly disabled.
|
add_cxx_compiler_flag(-fstrict-aliasing)
|
||||||
# See #631 for rationale.
|
# Disable warnings regarding deprecated parts of the library while building
|
||||||
add_cxx_compiler_flag(-wd1786)
|
# and testing those parts of the library.
|
||||||
endif()
|
add_cxx_compiler_flag(-Wno-deprecated-declarations)
|
||||||
# Disable deprecation warnings for release builds (when -Werror is enabled).
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "Intel" OR CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM")
|
||||||
add_cxx_compiler_flag(-Wno-deprecated RELEASE)
|
# Intel silently ignores '-Wno-deprecated-declarations',
|
||||||
add_cxx_compiler_flag(-Wno-deprecated RELWITHDEBINFO)
|
# warning no. 1786 must be explicitly disabled.
|
||||||
add_cxx_compiler_flag(-Wno-deprecated MINSIZEREL)
|
# See #631 for rationale.
|
||||||
if (NOT BENCHMARK_ENABLE_EXCEPTIONS)
|
add_cxx_compiler_flag(-wd1786)
|
||||||
add_cxx_compiler_flag(-fno-exceptions)
|
add_cxx_compiler_flag(-fno-finite-math-only)
|
||||||
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") #ICC17u2: Many false positives for Wstrict-aliasing
|
if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Intel" AND NOT CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM") #ICC17u2: Many false positives for Wstrict-aliasing
|
||||||
add_cxx_compiler_flag(-Wstrict-aliasing)
|
add_cxx_compiler_flag(-Wstrict-aliasing)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
@@ -173,21 +235,26 @@ 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)
|
cxx_feature_check(THREAD_SAFETY_ATTRIBUTES "-DINCLUDE_DIRECTORIES=${PROJECT_SOURCE_DIR}/include")
|
||||||
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 ourselfs
|
# However g++ doesn't do this in Cygwin so we have to define it ourselves
|
||||||
# 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)
|
||||||
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
|
add_cxx_compiler_flag(-Wno-lto-type-mismatch)
|
||||||
|
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})
|
||||||
@@ -196,7 +263,7 @@ else()
|
|||||||
if (GCC_RANLIB)
|
if (GCC_RANLIB)
|
||||||
set(CMAKE_RANLIB ${GCC_RANLIB})
|
set(CMAKE_RANLIB ${GCC_RANLIB})
|
||||||
endif()
|
endif()
|
||||||
elseif("${CMAKE_C_COMPILER_ID}" MATCHES "Clang")
|
elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||||
include(llvm-toolchain)
|
include(llvm-toolchain)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
@@ -224,7 +291,8 @@ 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")
|
"${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel" OR
|
||||||
|
"${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
|
||||||
@@ -250,9 +318,16 @@ 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)
|
||||||
@@ -262,8 +337,18 @@ add_subdirectory(src)
|
|||||||
|
|
||||||
if (BENCHMARK_ENABLE_TESTING)
|
if (BENCHMARK_ENABLE_TESTING)
|
||||||
enable_testing()
|
enable_testing()
|
||||||
if (BENCHMARK_ENABLE_GTEST_TESTS)
|
if (BENCHMARK_ENABLE_GTEST_TESTS AND
|
||||||
include(HandleGTest)
|
NOT (TARGET gtest AND TARGET gtest_main AND
|
||||||
|
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
+32
-1
@@ -22,44 +22,75 @@
|
|||||||
#
|
#
|
||||||
# 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
@@ -0,0 +1,41 @@
|
|||||||
|
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
+145
-904
File diff suppressed because it is too large
Load Diff
Vendored
+17
-4
@@ -1,7 +1,20 @@
|
|||||||
workspace(name = "com_github_google_benchmark")
|
workspace(name = "com_github_google_benchmark")
|
||||||
|
|
||||||
http_archive(
|
load("//:bazel/benchmark_deps.bzl", "benchmark_deps")
|
||||||
name = "com_google_googletest",
|
|
||||||
urls = ["https://github.com/google/googletest/archive/3f0cf6b62ad1eb50d8736538363d3580dd640c3e.zip"],
|
benchmark_deps()
|
||||||
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
@@ -0,0 +1,2 @@
|
|||||||
|
# This file marks the root of the Bazel workspace.
|
||||||
|
# See MODULE.bazel for dependencies and setup.
|
||||||
Vendored
+2
@@ -0,0 +1,2 @@
|
|||||||
|
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 -c %configuration% --timeout 300 --output-on-failure
|
- ctest --build-config %configuration% --timeout 300 --output-on-failure
|
||||||
|
|
||||||
artifacts:
|
artifacts:
|
||||||
- path: '_build/CMakeFiles/*.log'
|
- path: '_build/CMakeFiles/*.log'
|
||||||
|
|||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
"""
|
||||||
|
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"],
|
||||||
|
)
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
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",
|
||||||
|
],
|
||||||
|
)
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
# 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)
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
// 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
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
# 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()
|
||||||
+8
-4
@@ -34,9 +34,11 @@ 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})
|
||||||
set(VARIANT ${ARGV1})
|
if(ARGC GREATER 1)
|
||||||
if(ARGV1)
|
set(VARIANT ${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()
|
||||||
@@ -49,9 +51,11 @@ 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})
|
||||||
set(VARIANT ${ARGV1})
|
if(ARGC GREATER 1)
|
||||||
if(ARGV1)
|
set(VARIANT ${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)
|
||||||
|
|||||||
+27
-9
@@ -17,6 +17,8 @@ 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)
|
||||||
@@ -27,26 +29,38 @@ 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
|
||||||
CMAKE_FLAGS ${BENCHMARK_CXX_LINKER_FLAGS}
|
CXX_STANDARD 17
|
||||||
LINK_LIBRARIES ${BENCHMARK_CXX_LIBRARIES})
|
CXX_STANDARD_REQUIRED ON
|
||||||
|
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)
|
set(RUN_${FEATURE} 0 CACHE INTERNAL "")
|
||||||
else()
|
else()
|
||||||
set(RUN_${FEATURE} 1)
|
set(RUN_${FEATURE} 1 CACHE INTERNAL "")
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
message(STATUS "Performing Test ${FEATURE}")
|
message(STATUS "Compiling and running to 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
|
||||||
CMAKE_FLAGS ${BENCHMARK_CXX_LINKER_FLAGS}
|
CXX_STANDARD 17
|
||||||
LINK_LIBRARIES ${BENCHMARK_CXX_LIBRARIES})
|
CXX_STANDARD_REQUIRED ON
|
||||||
|
CMAKE_FLAGS ${FEATURE_CHECK_CMAKE_FLAGS}
|
||||||
|
LINK_LIBRARIES ${BENCHMARK_CXX_LIBRARIES}
|
||||||
|
COMPILE_OUTPUT_VARIABLE COMPILE_OUTPUT_VAR)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -56,7 +70,11 @@ function(cxx_feature_check FILE)
|
|||||||
add_definitions(-DHAVE_${VAR})
|
add_definitions(-DHAVE_${VAR})
|
||||||
else()
|
else()
|
||||||
if(NOT COMPILE_${FEATURE})
|
if(NOT COMPILE_${FEATURE})
|
||||||
message(STATUS "Performing Test ${FEATURE} -- failed to compile")
|
if(CXXFEATURECHECK_DEBUG)
|
||||||
|
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 +1,12 @@
|
|||||||
|
@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")
|
||||||
|
|||||||
+2
-20
@@ -20,35 +20,17 @@ 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 --match "v[0-9]*.[0-9]*.[0-9]*" --abbrev=8
|
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --match "v[0-9]*.[0-9]*.[0-9]*" --abbrev=8 --dirty
|
||||||
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
@@ -0,0 +1,58 @@
|
|||||||
|
# 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
@@ -0,0 +1,60 @@
|
|||||||
|
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}\")
|
||||||
|
")
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user