Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
56050de5cd | ||
|
|
772136decd | ||
|
|
f664601c51 | ||
|
|
f06b27b7fb | ||
|
|
ef53ce88d4 | ||
|
|
ef18030e1a | ||
|
|
f07176c0a2 | ||
|
|
15db141c7a | ||
|
|
d4bbb7c591 | ||
|
|
92e620d2ad | ||
|
|
311104da97 | ||
|
|
3127632f96 |
@@ -31,7 +31,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.google.android.filament:filament-android:1.68.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.68.0'
|
pod 'Filament', '~> 1.67.1'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|||||||
+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.68.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
@@ -30,6 +30,8 @@
|
|||||||
|
|
||||||
#include <math/mat3.h>
|
#include <math/mat3.h>
|
||||||
|
|
||||||
|
#include "AndroidNativeWindow.h"
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ public:
|
|||||||
|
|
||||||
MetalContext* getContext() { return mContext; }
|
MetalContext* getContext() { return mContext; }
|
||||||
|
|
||||||
|
using DriverBase::scheduleDestroy;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
friend class MetalSwapChain;
|
friend class MetalSwapChain;
|
||||||
|
|||||||
@@ -2275,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));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2283,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
|
||||||
|
|||||||
@@ -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 = 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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -780,7 +780,7 @@ public:
|
|||||||
bool assert_texture_can_generate_mipmap = CORRECTNESS_ASSERTION_DEFAULT;
|
bool assert_texture_can_generate_mipmap = CORRECTNESS_ASSERTION_DEFAULT;
|
||||||
} debug;
|
} debug;
|
||||||
struct {
|
struct {
|
||||||
bool disable_gpu_frame_complete_metric = true;
|
bool disable_gpu_frame_complete_metric = false;
|
||||||
} frame_info;
|
} frame_info;
|
||||||
} engine;
|
} engine;
|
||||||
struct {
|
struct {
|
||||||
|
|||||||
@@ -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.68.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.68.0/filament-v1.68.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 = 68;
|
static constexpr size_t MATERIAL_VERSION = 67;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Supported shading models
|
* Supported shading models
|
||||||
|
|||||||
@@ -190,47 +190,6 @@ 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();
|
||||||
|
|
||||||
@@ -269,7 +228,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.GetTexID();
|
auto texture = (Texture const*)pcmd.TextureId;
|
||||||
MaterialInstance* materialInstance;
|
MaterialInstance* materialInstance;
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
if (texture && texture->getTarget() == Texture::Sampler::SAMPLER_EXTERNAL) {
|
if (texture && texture->getTarget() == Texture::Sampler::SAMPLER_EXTERNAL) {
|
||||||
|
|||||||
@@ -37,13 +37,9 @@ set(SRCS
|
|||||||
src/IcoSphere.cpp
|
src/IcoSphere.cpp
|
||||||
src/MeshAssimp.cpp
|
src/MeshAssimp.cpp
|
||||||
src/Sphere.cpp
|
src/Sphere.cpp
|
||||||
src/KeyInputConversion.h
|
src/VulkanPlatformHelper.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if (FILAMENT_SUPPORTS_VULKAN)
|
|
||||||
list(APPEND SRCS src/VulkanPlatformHelper.cpp)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(LIBS
|
set(LIBS
|
||||||
assimp
|
assimp
|
||||||
camutils
|
camutils
|
||||||
@@ -83,11 +79,7 @@ endif()
|
|||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
list(APPEND SRCS src/NativeWindowHelperWindows.cpp)
|
list(APPEND SRCS src/NativeWindowHelperWindows.cpp)
|
||||||
|
list(APPEND SRCS src/VulkanPlatformHelperWindows.cpp)
|
||||||
if (FILAMENT_SUPPORTS_VULKAN)
|
|
||||||
list(APPEND SRCS src/VulkanPlatformHelperWindows.cpp)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
list(APPEND LIBS sdl2main)
|
list(APPEND LIBS sdl2main)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
#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>
|
||||||
@@ -213,6 +211,27 @@ 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);
|
||||||
};
|
};
|
||||||
@@ -286,19 +305,15 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
|
|||||||
io.AddInputCharactersUTF8(event->text.text);
|
io.AddInputCharactersUTF8(event->text.text);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDL_KEYUP:
|
case SDL_KEYDOWN:
|
||||||
case SDL_KEYDOWN: {
|
case SDL_KEYUP: {
|
||||||
SDL_Scancode const scancode = event->key.keysym.scancode;
|
int key = event->key.keysym.scancode;
|
||||||
SDL_Keycode const keycode = event->key.keysym.sym;
|
IM_ASSERT(key >= 0 && key < IM_ARRAYSIZE(io.KeysDown));
|
||||||
|
io.KeysDown[key] = (event->type == SDL_KEYDOWN);
|
||||||
auto modState = SDL_GetModState();
|
io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0);
|
||||||
io.AddKeyEvent(ImGuiMod_Ctrl, (modState & KMOD_CTRL) != 0);
|
io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0);
|
||||||
io.AddKeyEvent(ImGuiMod_Shift, (modState & KMOD_SHIFT) != 0);
|
io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0);
|
||||||
io.AddKeyEvent(ImGuiMod_Alt, (modState & KMOD_ALT) != 0);
|
io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0);
|
||||||
io.AddKeyEvent(ImGuiMod_Super, (modState & KMOD_GUI) != 0);
|
|
||||||
io.AddKeyEvent(
|
|
||||||
filamentapp_utils::ImGui_ImplSDL2_KeyEventToImGuiKey(keycode, scancode),
|
|
||||||
event->type == SDL_KEYDOWN);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,297 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2025 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef TNT_FILAMENT_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
|
|
||||||
@@ -55,10 +55,7 @@ AsyncJobQueue::AsyncJobQueue(const char* name, Priority priority) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
AsyncJobQueue::~AsyncJobQueue() noexcept {
|
AsyncJobQueue::~AsyncJobQueue() noexcept {
|
||||||
// wait for all pending callbacks to be called & terminate the thread
|
|
||||||
drainAndExit();
|
|
||||||
#if !defined(__EMSCRIPTEN__)
|
#if !defined(__EMSCRIPTEN__)
|
||||||
assert_invariant(!mThread.joinable());
|
|
||||||
assert_invariant(mQueue.empty());
|
assert_invariant(mQueue.empty());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -97,9 +94,6 @@ bool AsyncJobQueue::isValid() const noexcept {
|
|||||||
void AsyncJobQueue::drainAndExit() {
|
void AsyncJobQueue::drainAndExit() {
|
||||||
#if !defined(__EMSCRIPTEN__)
|
#if !defined(__EMSCRIPTEN__)
|
||||||
std::unique_lock lock(mLock);
|
std::unique_lock lock(mLock);
|
||||||
if (mExitRequested) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// we request the service thread to exit, but we're guaranteed that it'll only exit
|
// we request the service thread to exit, but we're guaranteed that it'll only exit
|
||||||
// after all current callbacks are processed. In addition, once mExitRequested is set,
|
// after all current callbacks are processed. In addition, once mExitRequested is set,
|
||||||
// no new jobs can be added, so we can join the thread.
|
// no new jobs can be added, so we can join the thread.
|
||||||
|
|||||||
@@ -41,36 +41,6 @@ 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;
|
||||||
@@ -593,6 +563,27 @@ 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.
|
||||||
@@ -618,28 +609,20 @@ void ViewerGui::mouseEvent(float mouseX, float mouseY, bool mouseButton, float m
|
|||||||
io.MouseDown[0] = mouseButton != 0;
|
io.MouseDown[0] = mouseButton != 0;
|
||||||
io.MouseDown[1] = false;
|
io.MouseDown[1] = false;
|
||||||
io.MouseDown[2] = false;
|
io.MouseDown[2] = false;
|
||||||
io.AddKeyEvent(ImGuiMod_Ctrl, control);
|
io.KeyCtrl = control;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewerGui::keyDownEvent(int keyCode) {
|
void ViewerGui::keyDownEvent(int keyCode) {
|
||||||
if (!mImGuiHelper) {
|
if (mImGuiHelper && keyCode < IM_ARRAYSIZE(ImGui::GetIO().KeysDown)) {
|
||||||
return;
|
ImGui::GetIO().KeysDown[keyCode] = true;
|
||||||
}
|
}
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
|
||||||
io.AddKeyEvent(
|
|
||||||
convertKey(keyCode),
|
|
||||||
true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewerGui::keyUpEvent(int keyCode) {
|
void ViewerGui::keyUpEvent(int keyCode) {
|
||||||
if (!mImGuiHelper) {
|
if (mImGuiHelper && keyCode < IM_ARRAYSIZE(ImGui::GetIO().KeysDown)) {
|
||||||
return;
|
ImGui::GetIO().KeysDown[keyCode] = false;
|
||||||
}
|
}
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
|
||||||
io.AddKeyEvent(
|
|
||||||
convertKey(keyCode),
|
|
||||||
false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewerGui::keyPressEvent(int charCode) {
|
void ViewerGui::keyPressEvent(int charCode) {
|
||||||
|
|||||||
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}\")
|
||||||
|
")
|
||||||
-113
@@ -1,113 +0,0 @@
|
|||||||
|
|
||||||
include(split_list)
|
|
||||||
|
|
||||||
macro(build_external_gtest)
|
|
||||||
include(ExternalProject)
|
|
||||||
set(GTEST_FLAGS "")
|
|
||||||
if (BENCHMARK_USE_LIBCXX)
|
|
||||||
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
|
||||||
list(APPEND GTEST_FLAGS -stdlib=libc++)
|
|
||||||
else()
|
|
||||||
message(WARNING "Unsupported compiler (${CMAKE_CXX_COMPILER}) when using libc++")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
if (BENCHMARK_BUILD_32_BITS)
|
|
||||||
list(APPEND GTEST_FLAGS -m32)
|
|
||||||
endif()
|
|
||||||
if (NOT "${CMAKE_CXX_FLAGS}" STREQUAL "")
|
|
||||||
list(APPEND GTEST_FLAGS ${CMAKE_CXX_FLAGS})
|
|
||||||
endif()
|
|
||||||
string(TOUPPER "${CMAKE_BUILD_TYPE}" GTEST_BUILD_TYPE)
|
|
||||||
if ("${GTEST_BUILD_TYPE}" STREQUAL "COVERAGE")
|
|
||||||
set(GTEST_BUILD_TYPE "DEBUG")
|
|
||||||
endif()
|
|
||||||
# FIXME: Since 10/Feb/2017 the googletest trunk has had a bug where
|
|
||||||
# -Werror=unused-function fires during the build on OS X. This is a temporary
|
|
||||||
# workaround to keep our travis bots from failing. It should be removed
|
|
||||||
# once gtest is fixed.
|
|
||||||
if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|
||||||
list(APPEND GTEST_FLAGS "-Wno-unused-function")
|
|
||||||
endif()
|
|
||||||
split_list(GTEST_FLAGS)
|
|
||||||
set(EXCLUDE_FROM_ALL_OPT "")
|
|
||||||
set(EXCLUDE_FROM_ALL_VALUE "")
|
|
||||||
if (${CMAKE_VERSION} VERSION_GREATER "3.0.99")
|
|
||||||
set(EXCLUDE_FROM_ALL_OPT "EXCLUDE_FROM_ALL")
|
|
||||||
set(EXCLUDE_FROM_ALL_VALUE "ON")
|
|
||||||
endif()
|
|
||||||
ExternalProject_Add(googletest
|
|
||||||
${EXCLUDE_FROM_ALL_OPT} ${EXCLUDE_FROM_ALL_VALUE}
|
|
||||||
GIT_REPOSITORY https://github.com/google/googletest.git
|
|
||||||
GIT_TAG master
|
|
||||||
PREFIX "${CMAKE_BINARY_DIR}/googletest"
|
|
||||||
INSTALL_DIR "${CMAKE_BINARY_DIR}/googletest"
|
|
||||||
CMAKE_CACHE_ARGS
|
|
||||||
-DCMAKE_BUILD_TYPE:STRING=${GTEST_BUILD_TYPE}
|
|
||||||
-DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER}
|
|
||||||
-DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER}
|
|
||||||
-DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
|
|
||||||
-DCMAKE_INSTALL_LIBDIR:PATH=<INSTALL_DIR>/lib
|
|
||||||
-DCMAKE_CXX_FLAGS:STRING=${GTEST_FLAGS}
|
|
||||||
-Dgtest_force_shared_crt:BOOL=ON
|
|
||||||
)
|
|
||||||
|
|
||||||
ExternalProject_Get_Property(googletest install_dir)
|
|
||||||
set(GTEST_INCLUDE_DIRS ${install_dir}/include)
|
|
||||||
file(MAKE_DIRECTORY ${GTEST_INCLUDE_DIRS})
|
|
||||||
|
|
||||||
set(LIB_SUFFIX "${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
|
||||||
set(LIB_PREFIX "${CMAKE_STATIC_LIBRARY_PREFIX}")
|
|
||||||
if("${GTEST_BUILD_TYPE}" STREQUAL "DEBUG")
|
|
||||||
set(LIB_SUFFIX "d${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Use gmock_main instead of gtest_main because it initializes gtest as well.
|
|
||||||
# Note: The libraries are listed in reverse order of their dependancies.
|
|
||||||
foreach(LIB gtest gmock gmock_main)
|
|
||||||
add_library(${LIB} UNKNOWN IMPORTED)
|
|
||||||
set_target_properties(${LIB} PROPERTIES
|
|
||||||
IMPORTED_LOCATION ${install_dir}/lib/${LIB_PREFIX}${LIB}${LIB_SUFFIX}
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES ${GTEST_INCLUDE_DIRS}
|
|
||||||
INTERFACE_LINK_LIBRARIES "${GTEST_BOTH_LIBRARIES}"
|
|
||||||
)
|
|
||||||
add_dependencies(${LIB} googletest)
|
|
||||||
list(APPEND GTEST_BOTH_LIBRARIES ${LIB})
|
|
||||||
endforeach()
|
|
||||||
endmacro(build_external_gtest)
|
|
||||||
|
|
||||||
if (BENCHMARK_ENABLE_GTEST_TESTS)
|
|
||||||
if (IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/googletest)
|
|
||||||
set(GTEST_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/googletest")
|
|
||||||
set(INSTALL_GTEST OFF CACHE INTERNAL "")
|
|
||||||
set(INSTALL_GMOCK OFF CACHE INTERNAL "")
|
|
||||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/googletest)
|
|
||||||
set(GTEST_BOTH_LIBRARIES gtest gmock gmock_main)
|
|
||||||
foreach(HEADER test mock)
|
|
||||||
# CMake 2.8 and older don't respect INTERFACE_INCLUDE_DIRECTORIES, so we
|
|
||||||
# have to add the paths ourselves.
|
|
||||||
set(HFILE g${HEADER}/g${HEADER}.h)
|
|
||||||
set(HPATH ${GTEST_ROOT}/google${HEADER}/include)
|
|
||||||
find_path(HEADER_PATH_${HEADER} ${HFILE}
|
|
||||||
NO_DEFAULT_PATHS
|
|
||||||
HINTS ${HPATH}
|
|
||||||
)
|
|
||||||
if (NOT HEADER_PATH_${HEADER})
|
|
||||||
message(FATAL_ERROR "Failed to find header ${HFILE} in ${HPATH}")
|
|
||||||
endif()
|
|
||||||
list(APPEND GTEST_INCLUDE_DIRS ${HEADER_PATH_${HEADER}})
|
|
||||||
endforeach()
|
|
||||||
elseif(BENCHMARK_DOWNLOAD_DEPENDENCIES)
|
|
||||||
build_external_gtest()
|
|
||||||
else()
|
|
||||||
find_package(GTest REQUIRED)
|
|
||||||
find_path(GMOCK_INCLUDE_DIRS gmock/gmock.h
|
|
||||||
HINTS ${GTEST_INCLUDE_DIRS})
|
|
||||||
if (NOT GMOCK_INCLUDE_DIRS)
|
|
||||||
message(FATAL_ERROR "Failed to find header gmock/gmock.h with hint ${GTEST_INCLUDE_DIRS}")
|
|
||||||
endif()
|
|
||||||
set(GTEST_INCLUDE_DIRS ${GTEST_INCLUDE_DIRS} ${GMOCK_INCLUDE_DIRS})
|
|
||||||
# FIXME: We don't currently require the gmock library to build the tests,
|
|
||||||
# and it's likely we won't find it, so we don't try. As long as we've
|
|
||||||
# found the gmock/gmock.h header and gtest_main that should be good enough.
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
+4
-3
@@ -1,11 +1,12 @@
|
|||||||
prefix=@CMAKE_INSTALL_PREFIX@
|
prefix=@CMAKE_INSTALL_PREFIX@
|
||||||
exec_prefix=${prefix}
|
exec_prefix=${prefix}
|
||||||
libdir=${prefix}/lib
|
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
|
||||||
includedir=${prefix}/include
|
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
|
||||||
|
|
||||||
Name: @PROJECT_NAME@
|
Name: @PROJECT_NAME@
|
||||||
Description: Google microbenchmark framework
|
Description: Google microbenchmark framework
|
||||||
Version: @VERSION@
|
Version: @NORMALIZED_VERSION@
|
||||||
|
|
||||||
Libs: -L${libdir} -lbenchmark
|
Libs: -L${libdir} -lbenchmark
|
||||||
|
Libs.private: -lpthread @BENCHMARK_PRIVATE_LINK_LIBRARIES@
|
||||||
Cflags: -I${includedir}
|
Cflags: -I${includedir}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
|
||||||
|
|
||||||
|
Name: @PROJECT_NAME@
|
||||||
|
Description: Google microbenchmark framework (with main() function)
|
||||||
|
Version: @NORMALIZED_VERSION@
|
||||||
|
Requires: benchmark
|
||||||
|
Libs: -L${libdir} -lbenchmark_main
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#include <pthread.h>
|
||||||
|
int main() {
|
||||||
|
cpu_set_t set;
|
||||||
|
CPU_ZERO(&set);
|
||||||
|
for (int i = 0; i < CPU_SETSIZE; ++i) {
|
||||||
|
CPU_SET(i, &set);
|
||||||
|
CPU_CLR(i, &set);
|
||||||
|
}
|
||||||
|
pthread_t self = pthread_self();
|
||||||
|
int ret;
|
||||||
|
ret = pthread_getaffinity_np(self, sizeof(set), &set);
|
||||||
|
if (ret != 0) return ret;
|
||||||
|
ret = pthread_setaffinity_np(self, sizeof(set), &set);
|
||||||
|
if (ret != 0) return ret;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
+2
@@ -111,6 +111,7 @@ between compilers or compiler versions. A common example of this
|
|||||||
is matching stack frame addresses. In this case regular expressions
|
is matching stack frame addresses. In this case regular expressions
|
||||||
can be used to match the differing bits of output. For example:
|
can be used to match the differing bits of output. For example:
|
||||||
|
|
||||||
|
<!-- {% raw %} -->
|
||||||
```c++
|
```c++
|
||||||
int ExternInt;
|
int ExternInt;
|
||||||
struct Point { int x, y, z; };
|
struct Point { int x, y, z; };
|
||||||
@@ -127,6 +128,7 @@ extern "C" void test_store_point() {
|
|||||||
// CHECK: ret
|
// CHECK: ret
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
<!-- {% endraw %} -->
|
||||||
|
|
||||||
## Current Requirements and Limitations
|
## Current Requirements and Limitations
|
||||||
|
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
theme: jekyll-theme-minimal
|
||||||
|
logo: /assets/images/icon_black.png
|
||||||
|
show_downloads: true
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
+19
@@ -0,0 +1,19 @@
|
|||||||
|
# Build tool dependency policy
|
||||||
|
|
||||||
|
We follow the [Foundational C++ support policy](https://opensource.google/documentation/policies/cplusplus-support) for our build tools. In
|
||||||
|
particular the ["Build Systems" section](https://opensource.google/documentation/policies/cplusplus-support#build-systems).
|
||||||
|
|
||||||
|
## CMake
|
||||||
|
|
||||||
|
The current supported version is CMake 3.10 as of 2023-08-10. Most modern
|
||||||
|
distributions include newer versions, for example:
|
||||||
|
|
||||||
|
* Ubuntu 20.04 provides CMake 3.16.3
|
||||||
|
* Debian 11.4 provides CMake 3.18.4
|
||||||
|
* Ubuntu 22.04 provides CMake 3.22.1
|
||||||
|
|
||||||
|
## Python
|
||||||
|
|
||||||
|
The Python bindings require Python 3.10+ as of v1.9.0 (2024-08-16) for installation from PyPI.
|
||||||
|
Building from source for older versions probably still works, though. See the [user guide](python_bindings.md) for details on how to build from source.
|
||||||
|
The minimum theoretically supported version is Python 3.8, since the used bindings generator (nanobind) only supports Python 3.8+.
|
||||||
Vendored
+12
@@ -0,0 +1,12 @@
|
|||||||
|
# Benchmark
|
||||||
|
|
||||||
|
* [Assembly Tests](AssemblyTests.md)
|
||||||
|
* [Dependencies](dependencies.md)
|
||||||
|
* [Perf Counters](perf_counters.md)
|
||||||
|
* [Platform Specific Build Instructions](platform_specific_build_instructions.md)
|
||||||
|
* [Python Bindings](python_bindings.md)
|
||||||
|
* [Random Interleaving](random_interleaving.md)
|
||||||
|
* [Reducing Variance](reducing_variance.md)
|
||||||
|
* [Releasing](releasing.md)
|
||||||
|
* [Tools](tools.md)
|
||||||
|
* [User Guide](user_guide.md)
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
<a name="perf-counters" />
|
||||||
|
|
||||||
|
# User-Requested Performance Counters
|
||||||
|
|
||||||
|
When running benchmarks, the user may choose to request collection of
|
||||||
|
performance counters. This may be useful in investigation scenarios - narrowing
|
||||||
|
down the cause of a regression; or verifying that the underlying cause of a
|
||||||
|
performance improvement matches expectations.
|
||||||
|
|
||||||
|
This feature is available if:
|
||||||
|
|
||||||
|
* The benchmark is run on an architecture featuring a Performance Monitoring
|
||||||
|
Unit (PMU),
|
||||||
|
* The benchmark is compiled with support for collecting counters. Currently,
|
||||||
|
this requires [libpfm](http://perfmon2.sourceforge.net/), which is built as a
|
||||||
|
dependency via Bazel.
|
||||||
|
|
||||||
|
The feature does not require modifying benchmark code. Counter collection is
|
||||||
|
handled at the boundaries where timer collection is also handled.
|
||||||
|
|
||||||
|
To opt-in:
|
||||||
|
* If using a Bazel build, add `--define pfm=1` to your build flags
|
||||||
|
* If using CMake:
|
||||||
|
* Install `libpfm4-dev`, e.g. `apt-get install libpfm4-dev`.
|
||||||
|
* Enable the CMake flag `BENCHMARK_ENABLE_LIBPFM` in `CMakeLists.txt`.
|
||||||
|
|
||||||
|
To use, pass a comma-separated list of counter names through the
|
||||||
|
`--benchmark_perf_counters` flag. The names are decoded through libpfm - meaning,
|
||||||
|
they are platform specific, but some (e.g. `CYCLES` or `INSTRUCTIONS`) are
|
||||||
|
mapped by libpfm to platform-specifics - see libpfm
|
||||||
|
[documentation](http://perfmon2.sourceforge.net/docs.html) for more details.
|
||||||
|
|
||||||
|
The counter values are reported back through the [User Counters](../README.md#custom-counters)
|
||||||
|
mechanism, meaning, they are available in all the formats (e.g. JSON) supported
|
||||||
|
by User Counters.
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# Platform Specific Build Instructions
|
||||||
|
|
||||||
|
## Building with GCC
|
||||||
|
|
||||||
|
When the library is built using GCC it is necessary to link with the pthread
|
||||||
|
library due to how GCC implements `std::thread`. Failing to link to pthread will
|
||||||
|
lead to runtime exceptions (unless you're using libc++), not linker errors. See
|
||||||
|
[issue #67](https://github.com/google/benchmark/issues/67) for more details. You
|
||||||
|
can link to pthread by adding `-pthread` to your linker command. Note, you can
|
||||||
|
also use `-lpthread`, but there are potential issues with ordering of command
|
||||||
|
line parameters if you use that.
|
||||||
|
|
||||||
|
On QNX, the pthread library is part of libc and usually included automatically
|
||||||
|
(see
|
||||||
|
[`pthread_create()`](https://www.qnx.com/developers/docs/7.1/index.html#com.qnx.doc.neutrino.lib_ref/topic/p/pthread_create.html)).
|
||||||
|
There's no separate pthread library to link.
|
||||||
|
|
||||||
|
## Building with Visual Studio 2015, 2017 or 2022
|
||||||
|
|
||||||
|
The `shlwapi` library (`-lshlwapi`) is required to support a call to `CPUInfo` which reads the registry. Either add `shlwapi.lib` under `[ Configuration Properties > Linker > Input ]`, or use the following:
|
||||||
|
|
||||||
|
```
|
||||||
|
// Alternatively, can add libraries using linker options.
|
||||||
|
|
||||||
|
// First, Add the path to the generated library files (directory containing the `benchmark.lib`) in `[Configuration Properties > Linker > General > Additional Library Directories]`. Then do the following:
|
||||||
|
#ifdef _WIN32
|
||||||
|
#pragma comment ( lib, "Shlwapi.lib" )
|
||||||
|
#ifdef _DEBUG
|
||||||
|
#pragma comment ( lib, "benchmark.lib" )
|
||||||
|
#else
|
||||||
|
#pragma comment ( lib, "benchmark.lib" )
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
```
|
||||||
|
|
||||||
|
When using the static library, make sure to add `BENCHMARK_STATIC_DEFINE` under `[Configuration Properties > C/C++ > Preprocessor > Preprocessor Definitions]`
|
||||||
|
|
||||||
|
Can also use the graphical version of CMake:
|
||||||
|
* Open `CMake GUI`.
|
||||||
|
* Under `Where to build the binaries`, same path as source plus `build`.
|
||||||
|
* Under `CMAKE_INSTALL_PREFIX`, same path as source plus `install`.
|
||||||
|
* Click `Configure`, `Generate`, `Open Project`.
|
||||||
|
* If build fails, try deleting entire directory and starting again, or unticking options to build less.
|
||||||
|
|
||||||
|
## Building with Intel 2015 Update 1 or Intel System Studio Update 4
|
||||||
|
|
||||||
|
See instructions for building with Visual Studio. Once built, right click on the solution and change the build to Intel.
|
||||||
|
|
||||||
|
## Building on Solaris
|
||||||
|
|
||||||
|
If you're running benchmarks on solaris, you'll want the kstat library linked in
|
||||||
|
too (`-lkstat`).
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
# Building and installing Python bindings
|
||||||
|
|
||||||
|
Python bindings are available as wheels on [PyPI](https://pypi.org/project/google-benchmark/) for importing and
|
||||||
|
using Google Benchmark directly in Python.
|
||||||
|
Currently, pre-built wheels exist for macOS (both ARM64 and Intel x86), Linux x86-64 and 64-bit Windows.
|
||||||
|
Supported Python versions are Python 3.8 - 3.12.
|
||||||
|
|
||||||
|
To install Google Benchmark's Python bindings, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m pip install --upgrade pip # for manylinux2014 support
|
||||||
|
python -m pip install google-benchmark
|
||||||
|
```
|
||||||
|
|
||||||
|
In order to keep your system Python interpreter clean, it is advisable to run these commands in a virtual
|
||||||
|
environment. See the [official Python documentation](https://docs.python.org/3/library/venv.html)
|
||||||
|
on how to create virtual environments.
|
||||||
|
|
||||||
|
To build a wheel directly from source, you can follow these steps:
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/google/benchmark.git
|
||||||
|
cd benchmark
|
||||||
|
# create a virtual environment and activate it
|
||||||
|
python3 -m venv venv --system-site-packages
|
||||||
|
source venv/bin/activate # .\venv\Scripts\Activate.ps1 on Windows
|
||||||
|
|
||||||
|
# upgrade Python's system-wide packages
|
||||||
|
python -m pip install --upgrade pip build
|
||||||
|
# builds the wheel and stores it in the directory "dist".
|
||||||
|
python -m build
|
||||||
|
```
|
||||||
|
|
||||||
|
NB: Building wheels from source requires Bazel. For platform-specific instructions on how to install Bazel,
|
||||||
|
refer to the [Bazel installation docs](https://bazel.build/install).
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<a name="interleaving" />
|
||||||
|
|
||||||
|
# Random Interleaving
|
||||||
|
|
||||||
|
[Random Interleaving](https://github.com/google/benchmark/issues/1051) is a
|
||||||
|
technique to lower run-to-run variance. It randomly interleaves repetitions of a
|
||||||
|
microbenchmark with repetitions from other microbenchmarks in the same benchmark
|
||||||
|
test. Data shows it is able to lower run-to-run variance by
|
||||||
|
[40%](https://github.com/google/benchmark/issues/1051) on average.
|
||||||
|
|
||||||
|
To use, you mainly need to set `--benchmark_enable_random_interleaving=true`,
|
||||||
|
and optionally specify non-zero repetition count `--benchmark_repetitions=9`
|
||||||
|
and optionally decrease the per-repetition time `--benchmark_min_time=0.1`.
|
||||||
+133
@@ -0,0 +1,133 @@
|
|||||||
|
# Reducing Variance
|
||||||
|
|
||||||
|
<a name="disabling-cpu-frequency-scaling" />
|
||||||
|
|
||||||
|
## Disabling CPU Frequency Scaling
|
||||||
|
|
||||||
|
If you see this error:
|
||||||
|
|
||||||
|
```
|
||||||
|
***WARNING*** CPU scaling is enabled, the benchmark real time measurements may be noisy and will incur extra overhead.
|
||||||
|
```
|
||||||
|
|
||||||
|
you might want to disable the CPU frequency scaling while running the
|
||||||
|
benchmark, as well as consider other ways to stabilize the performance of
|
||||||
|
your system while benchmarking.
|
||||||
|
|
||||||
|
Exactly how to do this depends on the Linux distribution,
|
||||||
|
desktop environment, and installed programs. Specific details are a moving
|
||||||
|
target, so we will not attempt to exhaustively document them here.
|
||||||
|
|
||||||
|
One simple option is to use the `cpupower` program to change the
|
||||||
|
performance governor to "performance". This tool is maintained along with
|
||||||
|
the Linux kernel and provided by your distribution.
|
||||||
|
|
||||||
|
It must be run as root, like this:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo cpupower frequency-set --governor performance
|
||||||
|
```
|
||||||
|
|
||||||
|
After this you can verify that all CPUs are using the performance governor
|
||||||
|
by running this command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cpupower frequency-info -o proc
|
||||||
|
```
|
||||||
|
|
||||||
|
The benchmarks you subsequently run will have less variance.
|
||||||
|
|
||||||
|
<a name="reducing-variance" />
|
||||||
|
|
||||||
|
## Disabling ASLR
|
||||||
|
|
||||||
|
If you see this error:
|
||||||
|
|
||||||
|
```
|
||||||
|
***WARNING*** ASLR is enabled, the results may have unreproducible noise in them.
|
||||||
|
```
|
||||||
|
|
||||||
|
you might want to disable the ASLR security hardening feature while running the
|
||||||
|
benchmark.
|
||||||
|
|
||||||
|
The simplest way is to add
|
||||||
|
```
|
||||||
|
benchmark::MaybeReenterWithoutASLR(argc, argv);
|
||||||
|
```
|
||||||
|
as the first line of your `main()` function. It will try to disable ASLR
|
||||||
|
for the current processor, and, if successful, re-execute the binary.
|
||||||
|
Note that `personality(2)` may be forbidden by e.g. seccomp (which happens
|
||||||
|
by default if you are running in a Docker container).
|
||||||
|
|
||||||
|
Note that if you link to `benchmark_main` already does that for you.
|
||||||
|
|
||||||
|
To globally disable ASLR on Linux, run
|
||||||
|
```
|
||||||
|
echo 0 > /proc/sys/kernel/randomize_va_space
|
||||||
|
```
|
||||||
|
|
||||||
|
To run a single benchmark with ASLR disabled on Linux, do:
|
||||||
|
```
|
||||||
|
setarch `uname -m` -R ./a_benchmark
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that for the information on how to disable ASLR on other operating systems,
|
||||||
|
please refer to their documentation.
|
||||||
|
|
||||||
|
## Reducing Variance in Benchmarks
|
||||||
|
|
||||||
|
The Linux CPU frequency governor [discussed
|
||||||
|
above](user_guide#disabling-cpu-frequency-scaling) is not the only source
|
||||||
|
of noise in benchmarks. Some, but not all, of the sources of variance
|
||||||
|
include:
|
||||||
|
|
||||||
|
1. On multi-core machines not all CPUs/CPU cores/CPU threads run the same
|
||||||
|
speed, so running a benchmark one time and then again may give a
|
||||||
|
different result depending on which CPU it ran on.
|
||||||
|
2. CPU scaling features that run on the CPU, like Intel's Turbo Boost and
|
||||||
|
AMD Turbo Core and Precision Boost, can temporarily change the CPU
|
||||||
|
frequency even when the using the "performance" governor on Linux.
|
||||||
|
3. Context switching between CPUs, or scheduling competition on the CPU the
|
||||||
|
benchmark is running on.
|
||||||
|
4. Intel Hyperthreading or AMD SMT causing the same issue as above.
|
||||||
|
5. Cache effects caused by code running on other CPUs.
|
||||||
|
6. Non-uniform memory architectures (NUMA).
|
||||||
|
|
||||||
|
These can cause variance in benchmarks results within a single run
|
||||||
|
(`--benchmark_repetitions=N`) or across multiple runs of the benchmark
|
||||||
|
program.
|
||||||
|
|
||||||
|
Reducing sources of variance is OS and architecture dependent, which is one
|
||||||
|
reason some companies maintain machines dedicated to performance testing.
|
||||||
|
|
||||||
|
Some of the easier and effective ways of reducing variance on a typical
|
||||||
|
Linux workstation are:
|
||||||
|
|
||||||
|
1. Use the performance governor as [discussed
|
||||||
|
above](user_guide#disabling-cpu-frequency-scaling).
|
||||||
|
1. Disable processor boosting by:
|
||||||
|
```sh
|
||||||
|
echo 0 | sudo tee /sys/devices/system/cpu/cpufreq/boost
|
||||||
|
```
|
||||||
|
See the Linux kernel's
|
||||||
|
[boost.txt](https://www.kernel.org/doc/Documentation/cpu-freq/boost.txt)
|
||||||
|
for more information.
|
||||||
|
2. Set the benchmark program's task affinity to a fixed cpu. For example:
|
||||||
|
```sh
|
||||||
|
taskset -c 0 ./mybenchmark
|
||||||
|
```
|
||||||
|
3. Disabling Hyperthreading/SMT. This can be done in the Bios or using the
|
||||||
|
`/sys` file system (see the LLVM project's [Benchmarking
|
||||||
|
tips](https://llvm.org/docs/Benchmarking.html)).
|
||||||
|
4. Close other programs that do non-trivial things based on timers, such as
|
||||||
|
your web browser, desktop environment, etc.
|
||||||
|
5. Reduce the working set of your benchmark to fit within the L1 cache, but
|
||||||
|
do be aware that this may lead you to optimize for an unrealistic
|
||||||
|
situation.
|
||||||
|
|
||||||
|
Further resources on this topic:
|
||||||
|
|
||||||
|
1. The LLVM project's [Benchmarking
|
||||||
|
tips](https://llvm.org/docs/Benchmarking.html).
|
||||||
|
1. The Arch Wiki [Cpu frequency
|
||||||
|
scaling](https://wiki.archlinux.org/title/CPU_frequency_scaling) page.
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
# How to release
|
||||||
|
|
||||||
|
* Make sure you're on main and synced to HEAD
|
||||||
|
* Ensure the project builds and tests run
|
||||||
|
* `parallel -j0 exec ::: test/*_test` can help ensure everything at least
|
||||||
|
passes
|
||||||
|
* Prepare release notes
|
||||||
|
* `git log $(git describe --abbrev=0 --tags)..HEAD` gives you the list of
|
||||||
|
commits between the last annotated tag and HEAD
|
||||||
|
* Pick the most interesting.
|
||||||
|
* Create one last commit that updates the version saved in `CMakeLists.txt`, `MODULE.bazel`,
|
||||||
|
and `bindings/python/google_benchmark/__init__.py` to the release version you're creating.
|
||||||
|
(This version will be used if benchmark is installed from the archive you'll be creating
|
||||||
|
in the next step.)
|
||||||
|
|
||||||
|
```
|
||||||
|
# CMakeLists.txt
|
||||||
|
project (benchmark VERSION 1.9.0 LANGUAGES CXX)
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
# MODULE.bazel
|
||||||
|
module(name = "com_github_google_benchmark", version="1.9.0")
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
# google_benchmark/__init__.py
|
||||||
|
__version__ = "1.9.0"
|
||||||
|
```
|
||||||
|
|
||||||
|
* Create a release through github's interface
|
||||||
|
* Note this will create a lightweight tag.
|
||||||
|
* Update this to an annotated tag:
|
||||||
|
* `git pull --tags`
|
||||||
|
* `git tag -a -f <tag> <tag>`
|
||||||
|
* `git push --force --tags origin`
|
||||||
|
* Confirm that the "Build and upload Python wheels" action runs to completion
|
||||||
|
* Run it manually if it hasn't run.
|
||||||
Vendored
+145
-1
@@ -4,7 +4,11 @@
|
|||||||
|
|
||||||
The `compare.py` can be used to compare the result of benchmarks.
|
The `compare.py` can be used to compare the result of benchmarks.
|
||||||
|
|
||||||
**NOTE**: the utility relies on the scipy package which can be installed using [these instructions](https://www.scipy.org/install.html).
|
### Dependencies
|
||||||
|
The utility relies on the [scipy](https://www.scipy.org) package which can be installed using pip:
|
||||||
|
```bash
|
||||||
|
pip3 install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
### Displaying aggregates only
|
### Displaying aggregates only
|
||||||
|
|
||||||
@@ -182,6 +186,146 @@ Benchmark Time CPU Time Old
|
|||||||
This is a mix of the previous two modes, two (potentially different) benchmark binaries are run, and a different filter is applied to each one.
|
This is a mix of the previous two modes, two (potentially different) benchmark binaries are run, and a different filter is applied to each one.
|
||||||
As you can note, the values in `Time` and `CPU` columns are calculated as `(new - old) / |old|`.
|
As you can note, the values in `Time` and `CPU` columns are calculated as `(new - old) / |old|`.
|
||||||
|
|
||||||
|
### Note: Interpreting the output
|
||||||
|
|
||||||
|
Performance measurements are an art, and performance comparisons are doubly so.
|
||||||
|
Results are often noisy and don't necessarily have large absolute differences to
|
||||||
|
them, so just by visual inspection, it is not at all apparent if two
|
||||||
|
measurements are actually showing a performance change or not. It is even more
|
||||||
|
confusing with multiple benchmark repetitions.
|
||||||
|
|
||||||
|
Thankfully, what we can do, is use statistical tests on the results to determine
|
||||||
|
whether the performance has statistically-significantly changed. `compare.py`
|
||||||
|
uses [Mann–Whitney U
|
||||||
|
test](https://en.wikipedia.org/wiki/Mann%E2%80%93Whitney_U_test), with a null
|
||||||
|
hypothesis being that there's no difference in performance.
|
||||||
|
|
||||||
|
**The below output is a summary of a benchmark comparison with statistics
|
||||||
|
provided for a multi-threaded process.**
|
||||||
|
```
|
||||||
|
Benchmark Time CPU Time Old Time New CPU Old CPU New
|
||||||
|
-----------------------------------------------------------------------------------------------------------------------------
|
||||||
|
benchmark/threads:1/process_time/real_time_pvalue 0.0000 0.0000 U Test, Repetitions: 27 vs 27
|
||||||
|
benchmark/threads:1/process_time/real_time_mean -0.1442 -0.1442 90 77 90 77
|
||||||
|
benchmark/threads:1/process_time/real_time_median -0.1444 -0.1444 90 77 90 77
|
||||||
|
benchmark/threads:1/process_time/real_time_stddev +0.3974 +0.3933 0 0 0 0
|
||||||
|
benchmark/threads:1/process_time/real_time_cv +0.6329 +0.6280 0 0 0 0
|
||||||
|
OVERALL_GEOMEAN -0.1442 -0.1442 0 0 0 0
|
||||||
|
```
|
||||||
|
--------------------------------------------
|
||||||
|
Here's a breakdown of each row:
|
||||||
|
|
||||||
|
**benchmark/threads:1/process_time/real_time_pvalue**: This shows the _p-value_ for
|
||||||
|
the statistical test comparing the performance of the process running with one
|
||||||
|
thread. A value of 0.0000 suggests a statistically significant difference in
|
||||||
|
performance. The comparison was conducted using the U Test (Mann-Whitney
|
||||||
|
U Test) with 27 repetitions for each case.
|
||||||
|
|
||||||
|
**benchmark/threads:1/process_time/real_time_mean**: This shows the relative
|
||||||
|
difference in mean execution time between two different cases. The negative
|
||||||
|
value (-0.1442) implies that the new process is faster by about 14.42%. The old
|
||||||
|
time was 90 units, while the new time is 77 units.
|
||||||
|
|
||||||
|
**benchmark/threads:1/process_time/real_time_median**: Similarly, this shows the
|
||||||
|
relative difference in the median execution time. Again, the new process is
|
||||||
|
faster by 14.44%.
|
||||||
|
|
||||||
|
**benchmark/threads:1/process_time/real_time_stddev**: This is the relative
|
||||||
|
difference in the standard deviation of the execution time, which is a measure
|
||||||
|
of how much variation or dispersion there is from the mean. A positive value
|
||||||
|
(+0.3974) implies there is more variance in the execution time in the new
|
||||||
|
process.
|
||||||
|
|
||||||
|
**benchmark/threads:1/process_time/real_time_cv**: CV stands for Coefficient of
|
||||||
|
Variation. It is the ratio of the standard deviation to the mean. It provides a
|
||||||
|
standardized measure of dispersion. An increase (+0.6329) indicates more
|
||||||
|
relative variability in the new process.
|
||||||
|
|
||||||
|
**OVERALL_GEOMEAN**: Geomean stands for geometric mean, a type of average that is
|
||||||
|
less influenced by outliers. The negative value indicates a general improvement
|
||||||
|
in the new process. However, given the values are all zero for the old and new
|
||||||
|
times, this seems to be a mistake or placeholder in the output.
|
||||||
|
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Let's first try to see what the different columns represent in the above
|
||||||
|
`compare.py` benchmarking output:
|
||||||
|
|
||||||
|
1. **Benchmark:** The name of the function being benchmarked, along with the
|
||||||
|
size of the input (after the slash).
|
||||||
|
|
||||||
|
2. **Time:** The average time per operation, across all iterations.
|
||||||
|
|
||||||
|
3. **CPU:** The average CPU time per operation, across all iterations.
|
||||||
|
|
||||||
|
4. **Iterations:** The number of iterations the benchmark was run to get a
|
||||||
|
stable estimate.
|
||||||
|
|
||||||
|
5. **Time Old and Time New:** These represent the average time it takes for a
|
||||||
|
function to run in two different scenarios or versions. For example, you
|
||||||
|
might be comparing how fast a function runs before and after you make some
|
||||||
|
changes to it.
|
||||||
|
|
||||||
|
6. **CPU Old and CPU New:** These show the average amount of CPU time that the
|
||||||
|
function uses in two different scenarios or versions. This is similar to
|
||||||
|
Time Old and Time New, but focuses on CPU usage instead of overall time.
|
||||||
|
|
||||||
|
In the comparison section, the relative differences in both time and CPU time
|
||||||
|
are displayed for each input size.
|
||||||
|
|
||||||
|
|
||||||
|
A statistically-significant difference is determined by a **p-value**, which is
|
||||||
|
a measure of the probability that the observed difference could have occurred
|
||||||
|
just by random chance. A smaller p-value indicates stronger evidence against the
|
||||||
|
null hypothesis.
|
||||||
|
|
||||||
|
**Therefore:**
|
||||||
|
1. If the p-value is less than the chosen significance level (alpha), we
|
||||||
|
reject the null hypothesis and conclude the benchmarks are significantly
|
||||||
|
different.
|
||||||
|
2. If the p-value is greater than or equal to alpha, we fail to reject the
|
||||||
|
null hypothesis and treat the two benchmarks as similar.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The result of said the statistical test is additionally communicated through color coding:
|
||||||
|
```diff
|
||||||
|
+ Green:
|
||||||
|
```
|
||||||
|
The benchmarks are _**statistically different**_. This could mean the
|
||||||
|
performance has either **significantly improved** or **significantly
|
||||||
|
deteriorated**. You should look at the actual performance numbers to see which
|
||||||
|
is the case.
|
||||||
|
```diff
|
||||||
|
- Red:
|
||||||
|
```
|
||||||
|
The benchmarks are _**statistically similar**_. This means the performance
|
||||||
|
**hasn't significantly changed**.
|
||||||
|
|
||||||
|
In statistical terms, **'green'** means we reject the null hypothesis that
|
||||||
|
there's no difference in performance, and **'red'** means we fail to reject the
|
||||||
|
null hypothesis. This might seem counter-intuitive if you're expecting 'green'
|
||||||
|
to mean 'improved performance' and 'red' to mean 'worsened performance'.
|
||||||
|
```bash
|
||||||
|
But remember, in this context:
|
||||||
|
|
||||||
|
'Success' means 'successfully finding a difference'.
|
||||||
|
'Failure' means 'failing to find a difference'.
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Also, please note that **even if** we determine that there **is** a
|
||||||
|
statistically-significant difference between the two measurements, it does not
|
||||||
|
_necessarily_ mean that the actual benchmarks that were measured **are**
|
||||||
|
different, or vice versa, even if we determine that there is **no**
|
||||||
|
statistically-significant difference between the two measurements, it does not
|
||||||
|
necessarily mean that the actual benchmarks that were measured **are not**
|
||||||
|
different.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### U test
|
### U test
|
||||||
|
|
||||||
If there is a sufficient repetition count of the benchmarks, the tool can do
|
If there is a sufficient repetition count of the benchmarks, the tool can do
|
||||||
|
|||||||
+1405
File diff suppressed because it is too large
Load Diff
+801
-302
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,47 @@
|
|||||||
|
#ifndef BENCHMARK_EXPORT_H
|
||||||
|
#define BENCHMARK_EXPORT_H
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#define EXPORT_ATTR __declspec(dllexport)
|
||||||
|
#define IMPORT_ATTR __declspec(dllimport)
|
||||||
|
#define NO_EXPORT_ATTR
|
||||||
|
#define DEPRECATED_ATTR __declspec(deprecated)
|
||||||
|
#else // _WIN32
|
||||||
|
#define EXPORT_ATTR __attribute__((visibility("default")))
|
||||||
|
#define IMPORT_ATTR __attribute__((visibility("default")))
|
||||||
|
#define NO_EXPORT_ATTR __attribute__((visibility("hidden")))
|
||||||
|
#define DEPRECATE_ATTR __attribute__((__deprecated__))
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
#ifdef BENCHMARK_STATIC_DEFINE
|
||||||
|
#define BENCHMARK_EXPORT
|
||||||
|
#define BENCHMARK_NO_EXPORT
|
||||||
|
#else // BENCHMARK_STATIC_DEFINE
|
||||||
|
#ifndef BENCHMARK_EXPORT
|
||||||
|
#ifdef benchmark_EXPORTS
|
||||||
|
/* We are building this library */
|
||||||
|
#define BENCHMARK_EXPORT EXPORT_ATTR
|
||||||
|
#else // benchmark_EXPORTS
|
||||||
|
/* We are using this library */
|
||||||
|
#define BENCHMARK_EXPORT IMPORT_ATTR
|
||||||
|
#endif // benchmark_EXPORTS
|
||||||
|
#endif // !BENCHMARK_EXPORT
|
||||||
|
|
||||||
|
#ifndef BENCHMARK_NO_EXPORT
|
||||||
|
#define BENCHMARK_NO_EXPORT NO_EXPORT_ATTR
|
||||||
|
#endif // !BENCHMARK_NO_EXPORT
|
||||||
|
#endif // BENCHMARK_STATIC_DEFINE
|
||||||
|
|
||||||
|
#ifndef BENCHMARK_DEPRECATED
|
||||||
|
#define BENCHMARK_DEPRECATED DEPRECATE_ATTR
|
||||||
|
#endif // BENCHMARK_DEPRECATED
|
||||||
|
|
||||||
|
#ifndef BENCHMARK_DEPRECATED_EXPORT
|
||||||
|
#define BENCHMARK_DEPRECATED_EXPORT BENCHMARK_EXPORT BENCHMARK_DEPRECATED
|
||||||
|
#endif // BENCHMARK_DEPRECATED_EXPORT
|
||||||
|
|
||||||
|
#ifndef BENCHMARK_DEPRECATED_NO_EXPORT
|
||||||
|
#define BENCHMARK_DEPRECATED_NO_EXPORT BENCHMARK_NO_EXPORT BENCHMARK_DEPRECATED
|
||||||
|
#endif // BENCHMARK_DEPRECATED_EXPORT
|
||||||
|
|
||||||
|
#endif /* BENCHMARK_EXPORT_H */
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user