Compare commits
51
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
531bfd6f6e | ||
|
|
db272484f9 | ||
|
|
9dadf400d2 | ||
|
|
16c7a3c968 | ||
|
|
626f338cba | ||
|
|
4e05cd73c6 | ||
|
|
49f9708312 | ||
|
|
2791195a92 | ||
|
|
65f78c06bb | ||
|
|
71175ee410 | ||
|
|
c9b310450c | ||
|
|
54c636d54c | ||
|
|
f503205be2 | ||
|
|
02106110fe | ||
|
|
5a0f78d915 | ||
|
|
f548c588d9 | ||
|
|
487fb273f1 | ||
|
|
111ac62d2f | ||
|
|
2fac13e154 | ||
|
|
bcae04c9ef | ||
|
|
55fc91b35f | ||
|
|
ff2827b226 | ||
|
|
13e1d2a042 | ||
|
|
b09cbaf0db | ||
|
|
b570d489e3 | ||
|
|
7f7daddbfa | ||
|
|
cfe6a423d3 | ||
|
|
ba99844f22 | ||
|
|
ea4c20ec2c | ||
|
|
0a661c1f3c | ||
|
|
021c4f6691 | ||
|
|
be6b6ae7be | ||
|
|
ad3a5b70b6 | ||
|
|
80803f9693 | ||
|
|
3b0e44c9a3 | ||
|
|
c5c2d42467 | ||
|
|
ff145f6ccf | ||
|
|
48bb10a4b5 | ||
|
|
01a2480175 | ||
|
|
9e921936c6 | ||
|
|
8bc4d3a9df | ||
|
|
0fdf1cac1d | ||
|
|
387942cc39 | ||
|
|
c81cedd887 | ||
|
|
abe8a49d17 | ||
|
|
96cba96058 | ||
|
|
4fccd957db | ||
|
|
d49554d5ce | ||
|
|
867161773e | ||
|
|
e2658506f6 | ||
|
|
6218164f79 |
@@ -6,3 +6,5 @@
|
||||
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
|
||||
|
||||
## Release notes for next branch cut
|
||||
|
||||
- engine: add `View::getLastDynamicResolutionScale()` (b/457753622)
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.68.0'
|
||||
implementation 'com.google.android.filament:filament-android:1.67.0'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
|
||||
iOS projects can use CocoaPods to install the latest release:
|
||||
|
||||
```shell
|
||||
pod 'Filament', '~> 1.68.0'
|
||||
pod 'Filament', '~> 1.67.0'
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -7,12 +7,6 @@ A new header is inserted each time a *tag* is created.
|
||||
Instead, if you are authoring a PR for the main branch, add your release note to
|
||||
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
|
||||
|
||||
## v1.68.0
|
||||
|
||||
- engine: add `View::getLastDynamicResolutionScale()` (b/457753622)
|
||||
- Metal: report GPU errors to the platform via `debugUpdateStat` (b/431665753).
|
||||
- materials: Make Material Instances' UBO descriptor use dynamic offsets. [⚠️ **Recompile Materials**]
|
||||
|
||||
## v1.67.1
|
||||
|
||||
- Metal: Add support for the `SwapChain::CONFIG_MSAA_4_SAMPLES` flag.
|
||||
|
||||
-1
@@ -22,7 +22,6 @@ object Utils {
|
||||
/**
|
||||
* Initializes the utils JNI layer. Must be called before using any utils functionality.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun init() {
|
||||
// Load Filament first to ensure that the NioUtils Java class is available in the JNIEnv.
|
||||
Filament.init()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.68.0
|
||||
VERSION_NAME=1.67.0
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
@@ -17,20 +17,12 @@ filamentTools {
|
||||
}
|
||||
|
||||
// don't forget to update MainACtivity.kt when/if changing this.
|
||||
tasks.register('copyDamagedHelmetGltf', Copy) {
|
||||
from file("../../../third_party/models/DamagedHelmet/DamagedHelmet.glb")
|
||||
into file("src/main/assets/models")
|
||||
rename {String fileName -> "helmet.glb"}
|
||||
}
|
||||
|
||||
// don't forget to update MainACtivity.kt when/if changing this.
|
||||
tasks.register('copyBusterGltf', Copy) {
|
||||
tasks.register('copyMesh', Copy) {
|
||||
from "../../../third_party/models/BusterDrone"
|
||||
into "src/main/assets/models"
|
||||
}
|
||||
|
||||
preBuild.dependsOn copyDamagedHelmetGltf
|
||||
preBuild.dependsOn copyBusterGltf
|
||||
preBuild.dependsOn copyMesh
|
||||
|
||||
clean.doFirst {
|
||||
delete "src/main/assets"
|
||||
|
||||
Binary file not shown.
@@ -214,6 +214,14 @@ if (FILAMENT_SUPPORTS_VULKAN)
|
||||
src/vulkan/VulkanMemory.h
|
||||
src/vulkan/VulkanPipelineCache.cpp
|
||||
src/vulkan/VulkanPipelineCache.h
|
||||
src/vulkan/VulkanPipelineStateSerializer.cpp
|
||||
src/vulkan/VulkanPipelineStateSerializer.h
|
||||
src/vulkan/VulkanSamplerStateSerializer.h
|
||||
src/vulkan/VulkanSamplerStateSerializer.cpp
|
||||
src/vulkan/VulkanRenderPassStateSerializer.h
|
||||
src/vulkan/VulkanRenderPassStateSerializer.cpp
|
||||
src/vulkan/VulkanDescriptorSetLayoutSerializer.h
|
||||
src/vulkan/VulkanDescriptorSetLayoutSerializer.cpp
|
||||
src/vulkan/VulkanPipelineLayoutCache.cpp
|
||||
src/vulkan/VulkanPipelineLayoutCache.h
|
||||
src/vulkan/VulkanQueryManager.cpp
|
||||
|
||||
@@ -19,17 +19,13 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_PLATFORM_H
|
||||
#define TNT_FILAMENT_BACKEND_PLATFORM_H
|
||||
|
||||
#include <utils/CString.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Invocable.h>
|
||||
#include <utils/Mutex.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -512,22 +508,13 @@ public:
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// Debugging APIs
|
||||
|
||||
using DebugUpdateStatFunc = utils::Invocable<void(const char* UTILS_NONNULL key,
|
||||
uint64_t intValue, utils::CString stringValue)>;
|
||||
using DebugUpdateStatFunc = utils::Invocable<void(const char* UTILS_NONNULL key, uint64_t value)>;
|
||||
|
||||
/**
|
||||
* Sets the callback function that the backend can use to update backend-specific statistics
|
||||
* to aid with debugging. This callback is guaranteed to be called on the Filament driver
|
||||
* thread.
|
||||
*
|
||||
* The callback signature is (key, intValue, stringValue). Note that for any given call,
|
||||
* only one of the value parameters (intValue or stringValue) will be meaningful, depending on
|
||||
* the specific key.
|
||||
*
|
||||
* IMPORTANT_NOTE: because the callback is called on the driver thread, only quick, non-blocking
|
||||
* work should be done inside it. Furthermore, no graphics API calls (such as GL calls) should
|
||||
* be made, which could interfere with Filament's driver state.
|
||||
*
|
||||
* @param debugUpdateStat an Invocable that updates debug statistics
|
||||
*/
|
||||
void setDebugUpdateStatFunc(DebugUpdateStatFunc&& debugUpdateStat) noexcept;
|
||||
@@ -546,32 +533,15 @@ public:
|
||||
* This function is guaranteed to be called only on a single thread, the Filament driver
|
||||
* thread.
|
||||
*
|
||||
* @param key a null-terminated C-string with the key of the debug statistic
|
||||
* @param intValue the updated integer value of key (the string value passed to the
|
||||
* callback will be empty)
|
||||
* @param key a null-terminated C-string with the key of the debug statistic
|
||||
* @param value the updated value of key
|
||||
*/
|
||||
void debugUpdateStat(const char* UTILS_NONNULL key, uint64_t intValue);
|
||||
|
||||
/**
|
||||
* To track backend-specific statistics, the backend implementation can call the
|
||||
* application-provided callback function debugUpdateStatFunc to associate or update a value
|
||||
* with a given key. It is possible for this function to be called multiple times with the
|
||||
* same key, in which case newer values should overwrite older values.
|
||||
*
|
||||
* This function is guaranteed to be called only on a single thread, the Filament driver
|
||||
* thread.
|
||||
*
|
||||
* @param key a null-terminated C-string with the key of the debug statistic
|
||||
* @param stringValue the updated string value of key (the integer value passed to the
|
||||
* callback will be 0)
|
||||
*/
|
||||
void debugUpdateStat(const char* UTILS_NONNULL key, utils::CString stringValue);
|
||||
void debugUpdateStat(const char* UTILS_NONNULL key, uint64_t value);
|
||||
|
||||
private:
|
||||
std::shared_ptr<InsertBlobFunc> mInsertBlob;
|
||||
std::shared_ptr<RetrieveBlobFunc> mRetrieveBlob;
|
||||
std::shared_ptr<DebugUpdateStatFunc> mDebugUpdateStat;
|
||||
mutable utils::Mutex mMutex;
|
||||
InsertBlobFunc mInsertBlob;
|
||||
RetrieveBlobFunc mRetrieveBlob;
|
||||
DebugUpdateStatFunc mDebugUpdateStat;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_ANDROID_H
|
||||
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_ANDROID_H
|
||||
|
||||
#include "AndroidSwapChainHelper.h"
|
||||
#include "AndroidFrameCallback.h"
|
||||
#include "AndroidNdk.h"
|
||||
|
||||
#include <backend/AcquiredImage.h>
|
||||
@@ -30,6 +32,8 @@
|
||||
|
||||
#include <math/mat3.h>
|
||||
|
||||
#include "AndroidNativeWindow.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include <stddef.h>
|
||||
@@ -167,10 +171,19 @@ protected:
|
||||
SwapChain* drawSwapChain,
|
||||
SwapChain* readSwapChain) override;
|
||||
|
||||
private:
|
||||
struct SwapChainEGLAndroid;
|
||||
struct AndroidDetails;
|
||||
struct SwapChainEGLAndroid : public SwapChainEGL {
|
||||
SwapChainEGLAndroid(PlatformEGLAndroid const& platform,
|
||||
void* nativeWindow, uint64_t flags);
|
||||
SwapChainEGLAndroid(PlatformEGLAndroid const& platform,
|
||||
uint32_t width, uint32_t height, uint64_t flags);
|
||||
void terminate(PlatformEGLAndroid& platform);
|
||||
bool setPresentFrameId(uint64_t frameId) const noexcept;
|
||||
uint64_t getFrameId(uint64_t frameId) const noexcept;
|
||||
private:
|
||||
AndroidSwapChainHelper mImpl{};
|
||||
};
|
||||
|
||||
private:
|
||||
// prevent derived classes' implementations to call through
|
||||
[[nodiscard]] SwapChain* createSwapChain(void* nativeWindow, uint64_t flags) override;
|
||||
[[nodiscard]] SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) override;
|
||||
@@ -190,14 +203,17 @@ private:
|
||||
|
||||
int mOSVersion;
|
||||
ExternalStreamManagerAndroid& mExternalStreamManager;
|
||||
AndroidDetails& mAndroidDetails;
|
||||
InitializeJvmForPerformanceManagerIfNeeded const mInitializeJvmForPerformanceManagerIfNeeded;
|
||||
utils::PerformanceHintManager mPerformanceHintManager;
|
||||
utils::PerformanceHintManager::Session mPerformanceHintSession;
|
||||
SwapChainEGLAndroid* mCurrentDrawSwapChain{};
|
||||
|
||||
using clock = std::chrono::high_resolution_clock;
|
||||
clock::time_point mStartTimeOfActualWork;
|
||||
SwapChainEGLAndroid* mCurrentDrawSwapChain{};
|
||||
AndroidProducerThrottling mProducerThrottling;
|
||||
bool mAssertNativeWindowIsValid = false;
|
||||
|
||||
AndroidFrameCallback mAndroidFrameCallback;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -444,11 +444,6 @@ protected:
|
||||
|
||||
virtual VkExternalFenceHandleTypeFlagBits getFenceExportFlags() const noexcept;
|
||||
|
||||
/**
|
||||
* Query if transient attachments are supported by the backend.
|
||||
*/
|
||||
bool isTransientAttachmentSupported() const noexcept;
|
||||
|
||||
private:
|
||||
friend struct VulkanPlatformPrivate;
|
||||
};
|
||||
|
||||
@@ -17,13 +17,12 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
|
||||
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
|
||||
|
||||
#include "AndroidFrameCallback.h"
|
||||
#include "AndroidNdk.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/platforms/VulkanPlatform.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <android/hardware_buffer.h>
|
||||
|
||||
namespace filament::backend {
|
||||
@@ -92,8 +91,6 @@ protected:
|
||||
VkExternalFenceHandleTypeFlagBits getFenceExportFlags() const noexcept override;
|
||||
|
||||
private:
|
||||
struct AndroidDetails;
|
||||
|
||||
struct ExternalImageVulkanAndroid : public ExternalImage {
|
||||
AHardwareBuffer* aHardwareBuffer = nullptr;
|
||||
bool sRGB = false;
|
||||
@@ -102,7 +99,7 @@ private:
|
||||
~ExternalImageVulkanAndroid() override;
|
||||
};
|
||||
|
||||
AndroidDetails& mAndroidDetails;
|
||||
AndroidFrameCallback mAndroidFrameCallback;
|
||||
int mOSVersion{};
|
||||
};
|
||||
|
||||
|
||||
@@ -61,16 +61,6 @@ int NativeWindow::enableFrameTimestamps(ANativeWindow* anw, bool enable) {
|
||||
return pWindow->perform(anw, ENABLE_FRAME_TIMESTAMPS, enable);
|
||||
}
|
||||
|
||||
int NativeWindow::frameTimestampsSupportsPresent(ANativeWindow* anw, bool* outSupportsPresent) {
|
||||
NativeWindow const* pWindow = reinterpret_cast<NativeWindow const*>(anw);
|
||||
int value = 0;
|
||||
bool const success = pWindow->perform(anw, FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value);
|
||||
if (success) {
|
||||
*outSupportsPresent = bool(value);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
int NativeWindow::getCompositorTiming(ANativeWindow* anw,
|
||||
int64_t* compositeDeadline, int64_t* compositeInterval,
|
||||
int64_t* compositeToPresentLatency) {
|
||||
|
||||
@@ -32,7 +32,6 @@ struct NativeWindow {
|
||||
// is valid query enum value
|
||||
enum {
|
||||
IS_VALID = 17,
|
||||
FRAME_TIMESTAMPS_SUPPORTS_PRESENT = 18,
|
||||
GET_NEXT_FRAME_ID = 24,
|
||||
ENABLE_FRAME_TIMESTAMPS = 25,
|
||||
GET_COMPOSITOR_TIMING = 26,
|
||||
@@ -52,7 +51,6 @@ struct NativeWindow {
|
||||
|
||||
static int getNextFrameId(ANativeWindow* anw, uint64_t* frameId);
|
||||
static int enableFrameTimestamps(ANativeWindow* anw, bool enable);
|
||||
static int frameTimestampsSupportsPresent(ANativeWindow* anw, bool* outSupportsPresent);
|
||||
static int getCompositorTiming(ANativeWindow* anw,
|
||||
int64_t* compositeDeadline, int64_t* compositeInterval,
|
||||
int64_t* compositeToPresentLatency);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <backend/platforms/AndroidNdk.h>
|
||||
#include "AndroidNdk.h"
|
||||
|
||||
#include <android/hardware_buffer.h>
|
||||
|
||||
|
||||
@@ -19,9 +19,6 @@
|
||||
|
||||
#include <android/native_window.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Logger.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
@@ -39,23 +36,11 @@ bool AndroidSwapChainHelper::setPresentFrameId(
|
||||
int const status = NativeWindow::getNextFrameId(anw, &sysFrameId);
|
||||
if (status == 0) {
|
||||
std::lock_guard const lock(mLock);
|
||||
// frameIds must be strictly monotonic, if that's not the case (i.e. the new frameId is
|
||||
// less or equal to the last one in the map), we have to clear the map, because the
|
||||
// map's find() assume sorted keys.
|
||||
// This case can happen if two different filament::Renderer are used with the same
|
||||
// ANativeWindow (the Renderer would have different frameIds). This is expected to
|
||||
// be a rare case.
|
||||
if (UTILS_UNLIKELY(!mFrameIdToSystemFrameId.empty() &&
|
||||
frameId <= mFrameIdToSystemFrameId.back().first)) {
|
||||
// this log is expected to happen very rarely
|
||||
DLOG(INFO) << "clearing frame history anw=" << anw
|
||||
<< ", frameId=" << frameId
|
||||
<< ", previous=" << mFrameIdToSystemFrameId.back().first
|
||||
<< ", sysFrameId=" << sysFrameId;
|
||||
// clear the frame history
|
||||
mFrameIdToSystemFrameId.clear();
|
||||
auto const pos = mFrameIdToSystemFrameId.find(frameId);
|
||||
if (pos && *pos != sysFrameId) {
|
||||
// we're trying to associate the same frame id to a different frame!
|
||||
return false;
|
||||
}
|
||||
|
||||
// oldest entry is removed
|
||||
mFrameIdToSystemFrameId.insert(frameId, sysFrameId);
|
||||
return true;
|
||||
|
||||
@@ -139,73 +139,42 @@ bool Platform::queryFrameTimestamps(SwapChain const*, uint64_t, FrameTimestamps*
|
||||
}
|
||||
|
||||
void Platform::setBlobFunc(InsertBlobFunc&& insertBlob, RetrieveBlobFunc&& retrieveBlob) noexcept {
|
||||
std::lock_guard<decltype(mMutex)> lock(mMutex);
|
||||
mInsertBlob = std::make_shared<InsertBlobFunc>(std::move(insertBlob));
|
||||
mRetrieveBlob = std::make_shared<RetrieveBlobFunc>(std::move(retrieveBlob));
|
||||
mInsertBlob = std::move(insertBlob);
|
||||
mRetrieveBlob = std::move(retrieveBlob);
|
||||
}
|
||||
|
||||
bool Platform::hasInsertBlobFunc() const noexcept {
|
||||
std::lock_guard<decltype(mMutex)> lock(mMutex);
|
||||
return mInsertBlob && bool(*mInsertBlob);
|
||||
return bool(mInsertBlob);
|
||||
}
|
||||
|
||||
bool Platform::hasRetrieveBlobFunc() const noexcept {
|
||||
std::lock_guard<decltype(mMutex)> lock(mMutex);
|
||||
return mRetrieveBlob && bool(*mRetrieveBlob);
|
||||
return bool(mRetrieveBlob);
|
||||
}
|
||||
|
||||
void Platform::insertBlob(void const* key, size_t keySize, void const* value, size_t valueSize) {
|
||||
std::shared_ptr<InsertBlobFunc> callback;
|
||||
{
|
||||
std::unique_lock<decltype(mMutex)> lock(mMutex);
|
||||
callback = mInsertBlob;
|
||||
}
|
||||
if (callback) {
|
||||
(*callback)(key, keySize, value, valueSize);
|
||||
if (mInsertBlob) {
|
||||
mInsertBlob(key, keySize, value, valueSize);
|
||||
}
|
||||
}
|
||||
|
||||
size_t Platform::retrieveBlob(void const* key, size_t keySize, void* value, size_t valueSize) {
|
||||
std::shared_ptr<RetrieveBlobFunc> callback;
|
||||
{
|
||||
std::unique_lock<decltype(mMutex)> lock(mMutex);
|
||||
callback = mRetrieveBlob;
|
||||
}
|
||||
if (callback) {
|
||||
return (*callback)(key, keySize, value, valueSize);
|
||||
if (mRetrieveBlob) {
|
||||
return mRetrieveBlob(key, keySize, value, valueSize);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Platform::setDebugUpdateStatFunc(DebugUpdateStatFunc&& debugUpdateStat) noexcept {
|
||||
std::lock_guard<decltype(mMutex)> lock(mMutex);
|
||||
mDebugUpdateStat = std::make_shared<DebugUpdateStatFunc>(std::move(debugUpdateStat));
|
||||
mDebugUpdateStat = std::move(debugUpdateStat);
|
||||
}
|
||||
|
||||
bool Platform::hasDebugUpdateStatFunc() const noexcept {
|
||||
std::lock_guard<decltype(mMutex)> lock(mMutex);
|
||||
return mDebugUpdateStat && bool(*mDebugUpdateStat);
|
||||
return bool(mDebugUpdateStat);
|
||||
}
|
||||
|
||||
void Platform::debugUpdateStat(const char* key, uint64_t intValue) {
|
||||
std::shared_ptr<DebugUpdateStatFunc> callback;
|
||||
{
|
||||
std::unique_lock<decltype(mMutex)> lock(mMutex);
|
||||
callback = mDebugUpdateStat;
|
||||
}
|
||||
if (callback) {
|
||||
(*callback)(key, intValue, "");
|
||||
}
|
||||
}
|
||||
|
||||
void Platform::debugUpdateStat(const char* key, utils::CString stringValue) {
|
||||
std::shared_ptr<DebugUpdateStatFunc> callback;
|
||||
{
|
||||
std::unique_lock<decltype(mMutex)> lock(mMutex);
|
||||
callback = mDebugUpdateStat;
|
||||
}
|
||||
if (callback) {
|
||||
(*callback)(key, 0, stringValue);
|
||||
void Platform::debugUpdateStat(const char* key, uint64_t value) {
|
||||
if (mDebugUpdateStat) {
|
||||
mDebugUpdateStat(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#ifndef TNT_METALCONTEXT_H
|
||||
#define TNT_METALCONTEXT_H
|
||||
|
||||
#include "MetalErrorQueue.h"
|
||||
#include "MetalResourceTracker.h"
|
||||
#include "MetalShaderCompiler.h"
|
||||
#include "MetalState.h"
|
||||
@@ -130,7 +129,6 @@ struct MetalContext {
|
||||
id<MTLCommandBuffer> pendingCommandBuffer = nil;
|
||||
id<MTLRenderCommandEncoder> currentRenderPassEncoder = nil;
|
||||
uint32_t currentFrame = 0;
|
||||
MetalErrorQueue commandBufferErrors;
|
||||
|
||||
std::atomic<bool> memorylessLimitsReached = false;
|
||||
|
||||
|
||||
@@ -153,7 +153,6 @@ id<MTLCommandBuffer> getPendingCommandBuffer(MetalContext* context) {
|
||||
|
||||
if (UTILS_UNLIKELY(errorCode != MTLCommandBufferErrorNone)) {
|
||||
logMTLCommandBufferError(errorCode);
|
||||
context->commandBufferErrors.push(buffer.error);
|
||||
}
|
||||
}];
|
||||
FILAMENT_CHECK_POSTCONDITION(context->pendingCommandBuffer)
|
||||
|
||||
@@ -254,18 +254,6 @@ MetalDriver::~MetalDriver() noexcept {
|
||||
void MetalDriver::tick(int) {
|
||||
executeTickOps();
|
||||
executeDeferredOps();
|
||||
|
||||
// Notify platform of GPU errors.
|
||||
auto& platform = mPlatform;
|
||||
if (UTILS_UNLIKELY(!mContext->commandBufferErrors.isEmpty())) {
|
||||
mContext->commandBufferErrors.flush([&platform](NSError* error) {
|
||||
if (UTILS_VERY_UNLIKELY(!error)) {
|
||||
return;
|
||||
}
|
||||
const utils::CString errorString(error.localizedDescription.UTF8String);
|
||||
platform.debugUpdateStat("filament.metal.command_buffer_error", errorString);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void MetalDriver::beginFrame(int64_t monotonic_clock_ns,
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_DRIVER_METALERRORQUEUE_H
|
||||
#define TNT_FILAMENT_DRIVER_METALERRORQUEUE_H
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
class MetalErrorQueue {
|
||||
public:
|
||||
bool isEmpty() const {
|
||||
return !mHasErrors.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void push(NSError* error) {
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
mErrors.push_back(error);
|
||||
mHasErrors.store(true, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void flush(const std::function<void(NSError*)>& callback) {
|
||||
if (UTILS_LIKELY(isEmpty())) {
|
||||
return;
|
||||
}
|
||||
std::vector<NSError*> errors;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
std::swap(mErrors, errors);
|
||||
mHasErrors.store(false, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
for (const auto& error: errors) {
|
||||
callback(error);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<NSError*> mErrors;
|
||||
std::mutex mMutex;
|
||||
|
||||
// Optimization to avoid locking the mutex at each call to flush.
|
||||
std::atomic<bool> mHasErrors;
|
||||
};
|
||||
|
||||
|
||||
#endif // TNT_FILAMENT_DRIVER_METALERRORQUEUE_H
|
||||
@@ -28,13 +28,14 @@
|
||||
#include <private/backend/VirtualMachineEnv.h>
|
||||
|
||||
#include "AndroidNativeWindow.h"
|
||||
#include "AndroidFrameCallback.h"
|
||||
#include "AndroidSwapChainHelper.h"
|
||||
#include "ExternalStreamManagerAndroid.h"
|
||||
|
||||
#include <android/api-level.h>
|
||||
#include <android/native_window.h>
|
||||
#include <android/hardware_buffer.h>
|
||||
#include <android/choreographer.h>
|
||||
#include <android/looper.h>
|
||||
|
||||
#include <utils/android/PerformanceHintManager.h>
|
||||
#include <utils/compiler.h>
|
||||
@@ -98,27 +99,6 @@ using namespace glext;
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
struct PlatformEGLAndroid::SwapChainEGLAndroid : public SwapChainEGL {
|
||||
SwapChainEGLAndroid(PlatformEGLAndroid const& platform,
|
||||
void* nativeWindow, uint64_t flags);
|
||||
SwapChainEGLAndroid(PlatformEGLAndroid const& platform,
|
||||
uint32_t width, uint32_t height, uint64_t flags);
|
||||
void terminate(PlatformEGLAndroid& platform);
|
||||
bool setPresentFrameId(uint64_t frameId) const noexcept;
|
||||
uint64_t getFrameId(uint64_t frameId) const noexcept;
|
||||
bool compositorTimingSupported = false;
|
||||
bool frameTimestampsSupported = false;
|
||||
private:
|
||||
AndroidSwapChainHelper mImpl{};
|
||||
};
|
||||
|
||||
struct PlatformEGLAndroid::AndroidDetails {
|
||||
AndroidProducerThrottling producerThrottling;
|
||||
AndroidFrameCallback androidFrameCallback;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
PlatformEGLAndroid::InitializeJvmForPerformanceManagerIfNeeded::InitializeJvmForPerformanceManagerIfNeeded() {
|
||||
// PerformanceHintManager() needs the calling thread to be a Java thread; so we need
|
||||
// to attach this thread to the JVM before we initialize PerformanceHintManager.
|
||||
@@ -132,20 +112,17 @@ PlatformEGLAndroid::InitializeJvmForPerformanceManagerIfNeeded::InitializeJvmFor
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
PlatformEGLAndroid::PlatformEGLAndroid() noexcept
|
||||
: mExternalStreamManager(ExternalStreamManagerAndroid::create()),
|
||||
mAndroidDetails(*(new(std::nothrow) AndroidDetails{})) {
|
||||
: mExternalStreamManager(ExternalStreamManagerAndroid::create()) {
|
||||
mOSVersion = android_get_device_api_level();
|
||||
if (mOSVersion < 0) {
|
||||
mOSVersion = __ANDROID_API_FUTURE__;
|
||||
}
|
||||
}
|
||||
|
||||
PlatformEGLAndroid::~PlatformEGLAndroid() noexcept {
|
||||
delete &mAndroidDetails;
|
||||
}
|
||||
PlatformEGLAndroid::~PlatformEGLAndroid() noexcept = default;
|
||||
|
||||
void PlatformEGLAndroid::terminate() noexcept {
|
||||
mAndroidDetails.androidFrameCallback.terminate();
|
||||
mAndroidFrameCallback.terminate();
|
||||
ExternalStreamManagerAndroid::destroy(&mExternalStreamManager);
|
||||
PlatformEGL::terminate();
|
||||
}
|
||||
@@ -230,9 +207,9 @@ Driver* PlatformEGLAndroid::createDriver(void* sharedContext,
|
||||
"eglGetNativeClientBufferANDROID"));
|
||||
|
||||
if (ext.egl.ANDROID_presentation_time) {
|
||||
eglPresentationTimeANDROID =
|
||||
PFNEGLPRESENTATIONTIMEANDROIDPROC(eglGetProcAddress(
|
||||
"eglPresentationTimeANDROID"));
|
||||
eglGetNativeClientBufferANDROID =
|
||||
PFNEGLGETNATIVECLIENTBUFFERANDROIDPROC(eglGetProcAddress(
|
||||
"eglGetNativeClientBufferANDROID"));
|
||||
}
|
||||
|
||||
if (ext.egl.ANDROID_get_frame_timestamps) {
|
||||
@@ -263,7 +240,7 @@ Driver* PlatformEGLAndroid::createDriver(void* sharedContext,
|
||||
|
||||
mAssertNativeWindowIsValid = driverConfig.assertNativeWindowIsValid;
|
||||
|
||||
mAndroidDetails.androidFrameCallback.init();
|
||||
mAndroidFrameCallback.init();
|
||||
|
||||
return driver;
|
||||
}
|
||||
@@ -287,25 +264,15 @@ bool PlatformEGLAndroid::queryCompositorTiming(SwapChain const* swapchain,
|
||||
}
|
||||
|
||||
AndroidFrameCallback::Timeline const preferredTimeline{
|
||||
mAndroidDetails.androidFrameCallback.getPreferredTimeline() };
|
||||
mAndroidFrameCallback.getPreferredTimeline() };
|
||||
outCompositorTiming->frameTime = preferredTimeline.frameTime;
|
||||
outCompositorTiming->expectedPresentTime = preferredTimeline.expectedPresentTime;
|
||||
outCompositorTiming->frameTimelineDeadline = preferredTimeline.frameTimelineDeadline;
|
||||
outCompositorTiming->compositeDeadline = CompositorTiming::INVALID;
|
||||
outCompositorTiming->compositeInterval = CompositorTiming::INVALID;
|
||||
outCompositorTiming->compositeToPresentLatency = CompositorTiming::INVALID;
|
||||
|
||||
// From this point on, we always return "success" because some timings were returned.
|
||||
|
||||
if (!static_cast<SwapChainEGLAndroid const *>(swapchain)->compositorTimingSupported) {
|
||||
// if this surface doesn't support it, don't attempt to query the values.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (UTILS_LIKELY(ext.egl.ANDROID_get_frame_timestamps)) {
|
||||
EGLSurface const sur = static_cast<SwapChainEGL const *>(swapchain)->sur;
|
||||
if (sur == EGL_NO_SURFACE) {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::array<EGLnsecsANDROID, 3> values;
|
||||
@@ -316,16 +283,26 @@ bool PlatformEGLAndroid::queryCompositorTiming(SwapChain const* swapchain,
|
||||
};
|
||||
EGLBoolean const success = eglGetCompositorTimingANDROID(getEglDisplay(), sur,
|
||||
names.size(), names.data(), values.data());
|
||||
if (UTILS_UNLIKELY(!success)) {
|
||||
// reset current error to EGL_SUCCESS
|
||||
eglGetError();
|
||||
} else {
|
||||
outCompositorTiming->compositeDeadline = values[0];
|
||||
outCompositorTiming->compositeInterval = values[1];
|
||||
outCompositorTiming->compositeToPresentLatency = values[2];
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
outCompositorTiming->compositeDeadline = values[0];
|
||||
outCompositorTiming->compositeInterval = values[1];
|
||||
outCompositorTiming->compositeToPresentLatency = values[2];
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
|
||||
// fallback to private APIs
|
||||
auto const anw = static_cast<SwapChainEGL const *>(swapchain)->nativeWindow;
|
||||
int const status = NativeWindow::getCompositorTiming(anw,
|
||||
&outCompositorTiming->compositeDeadline,
|
||||
&outCompositorTiming->compositeInterval,
|
||||
&outCompositorTiming->compositeToPresentLatency);
|
||||
if (status == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return PlatformEGL::queryCompositorTiming(swapchain, outCompositorTiming);
|
||||
}
|
||||
|
||||
bool PlatformEGLAndroid::setPresentFrameId(SwapChain const* swapchain,
|
||||
@@ -350,10 +327,6 @@ bool PlatformEGLAndroid::queryFrameTimestamps(SwapChain const* swapchain, uint64
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!static_cast<SwapChainEGLAndroid const *>(swapchain)->frameTimestampsSupported) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (UTILS_LIKELY(ext.egl.ANDROID_get_frame_timestamps)) {
|
||||
EGLSurface const sur = sc->sur;
|
||||
if (sur == EGL_NO_SURFACE) {
|
||||
@@ -374,9 +347,7 @@ bool PlatformEGLAndroid::queryFrameTimestamps(SwapChain const* swapchain, uint64
|
||||
};
|
||||
EGLBoolean const success = eglGetFrameTimestampsANDROID(getEglDisplay(), sur, hwFrameId,
|
||||
names.size(), names.data(), values.data());
|
||||
if (UTILS_UNLIKELY(!success)) {
|
||||
// reset current error to EGL_SUCCESS
|
||||
eglGetError();
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
outFrameTimestamps->requestedPresentTime = values[0];
|
||||
@@ -390,44 +361,28 @@ bool PlatformEGLAndroid::queryFrameTimestamps(SwapChain const* swapchain, uint64
|
||||
outFrameTimestamps->releaseTime = values[8];
|
||||
return true;
|
||||
}
|
||||
|
||||
// fallback to private APIs
|
||||
auto const anw = sc->nativeWindow;
|
||||
int const status = NativeWindow::getFrameTimestamps(anw, hwFrameId,
|
||||
&outFrameTimestamps->requestedPresentTime,
|
||||
&outFrameTimestamps->acquireTime,
|
||||
&outFrameTimestamps->latchTime,
|
||||
&outFrameTimestamps->firstCompositionStartTime,
|
||||
&outFrameTimestamps->lastCompositionStartTime,
|
||||
&outFrameTimestamps->gpuCompositionDoneTime,
|
||||
&outFrameTimestamps->displayPresentTime,
|
||||
&outFrameTimestamps->dequeueReadyTime,
|
||||
&outFrameTimestamps->releaseTime);
|
||||
if (status == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return PlatformEGL::queryFrameTimestamps(swapchain, frameId, outFrameTimestamps);
|
||||
}
|
||||
|
||||
Platform::SwapChain* PlatformEGLAndroid::createSwapChain(void* nativeWindow, uint64_t const flags) {
|
||||
auto* const sc = new(std::nothrow) SwapChainEGLAndroid(*this, nativeWindow, flags);
|
||||
if (UTILS_LIKELY(ext.egl.ANDROID_get_frame_timestamps)) {
|
||||
EGLDisplay const dpy = getEglDisplay();
|
||||
sc->compositorTimingSupported =
|
||||
eglGetCompositorTimingSupportedANDROID(dpy, sc->sur,
|
||||
EGL_COMPOSITE_DEADLINE_ANDROID) &&
|
||||
eglGetCompositorTimingSupportedANDROID(dpy, sc->sur,
|
||||
EGL_COMPOSITE_INTERVAL_ANDROID) &&
|
||||
eglGetCompositorTimingSupportedANDROID(dpy, sc->sur,
|
||||
EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID);
|
||||
sc->frameTimestampsSupported =
|
||||
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
|
||||
EGL_REQUESTED_PRESENT_TIME_ANDROID) &&
|
||||
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
|
||||
EGL_RENDERING_COMPLETE_TIME_ANDROID) &&
|
||||
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
|
||||
EGL_COMPOSITION_LATCH_TIME_ANDROID) &&
|
||||
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
|
||||
EGL_FIRST_COMPOSITION_START_TIME_ANDROID) &&
|
||||
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
|
||||
EGL_LAST_COMPOSITION_START_TIME_ANDROID) &&
|
||||
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
|
||||
EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID) &&
|
||||
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
|
||||
EGL_DISPLAY_PRESENT_TIME_ANDROID) &&
|
||||
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
|
||||
EGL_DEQUEUE_READY_TIME_ANDROID) &&
|
||||
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
|
||||
EGL_READS_DONE_TIME_ANDROID);
|
||||
}
|
||||
// This is expected to be a low frequency log, only turned on in debug builds
|
||||
DLOG(INFO) << "anw: " << nativeWindow
|
||||
<< ", compositorTimingSupported=" << sc->compositorTimingSupported
|
||||
<< ", frameTimestampsSupported=" << sc->frameTimestampsSupported;
|
||||
return sc;
|
||||
}
|
||||
|
||||
@@ -720,12 +675,12 @@ AcquiredImage PlatformEGLAndroid::transformAcquiredImage(AcquiredImage const sou
|
||||
|
||||
|
||||
bool PlatformEGLAndroid::isProducerThrottlingControlSupported() const {
|
||||
return mAndroidDetails.producerThrottling.isSupported();
|
||||
return mProducerThrottling.isSupported();
|
||||
}
|
||||
|
||||
int32_t PlatformEGLAndroid::setProducerThrottlingEnabled(
|
||||
EGLNativeWindowType const nativeWindow, bool const enabled) const {
|
||||
return mAndroidDetails.producerThrottling.setProducerThrottlingEnabled(nativeWindow, enabled);
|
||||
return mProducerThrottling.setProducerThrottlingEnabled(nativeWindow, enabled);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
@@ -749,6 +704,8 @@ PlatformEGLAndroid::SwapChainEGLAndroid::SwapChainEGLAndroid(PlatformEGLAndroid
|
||||
// we ignore the result, it doesn't matter much if it fails
|
||||
eglSurfaceAttrib(platform.getEglDisplay(), sur, EGL_TIMESTAMPS_ANDROID, EGL_TRUE);
|
||||
}
|
||||
} else {
|
||||
NativeWindow::enableFrameTimestamps(EGLNativeWindowType(nativeWindow), true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -214,10 +214,4 @@ constexpr static const int FVK_MAX_PIPELINE_AGE = FVK_MAX_COMMAND_BUFFERS;
|
||||
// destroying any unused pipeline object.
|
||||
static_assert(FVK_MAX_PIPELINE_AGE >= FVK_MAX_COMMAND_BUFFERS);
|
||||
|
||||
// Indicates if the backend must be setup to allow doing a RenderDoc capture.
|
||||
//
|
||||
// If this is true, the features not supported by RenderDoc must be disabled, otherwise
|
||||
// when using RenderDoc the application will crash or will fail to do a capture.
|
||||
constexpr static const int FVK_RENDERDOC_CAPTURE_MODE = false;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -154,10 +154,6 @@ public:
|
||||
return mStagingBufferBypassEnabled;
|
||||
}
|
||||
|
||||
inline bool pipelineCreationFeedbackSupported() const noexcept {
|
||||
return mPipelineCreationFeedbackSupported;
|
||||
}
|
||||
|
||||
private:
|
||||
VkPhysicalDeviceMemoryProperties mMemoryProperties = {};
|
||||
VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = {
|
||||
@@ -185,7 +181,6 @@ private:
|
||||
bool mProtectedMemorySupported = false;
|
||||
bool mIsUnifiedMemoryArchitecture = false;
|
||||
bool mStagingBufferBypassEnabled = false;
|
||||
bool mPipelineCreationFeedbackSupported = false;
|
||||
|
||||
fvkutils::VkFormatList mDepthStencilFormats;
|
||||
fvkutils::VkFormatList mBlittableDepthStencilFormats;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include "VulkanDescriptorSetLayoutCache.h"
|
||||
#include "VulkanDescriptorSetLayoutSerializer.h"
|
||||
|
||||
#include "VulkanHandles.h"
|
||||
|
||||
@@ -100,22 +101,32 @@ uint32_t appendSamplerBindings(VkDescriptorSetLayoutBinding* toBind,
|
||||
return count;
|
||||
}
|
||||
|
||||
uint64_t computeImmutableSamplerHash(utils::FixedCapacityVector<VkSampler> const& samplers) {
|
||||
uint64_t computeImmutableSamplerHash(utils::FixedCapacityVector<VkSampler> const& samplers, VulkanSamplerCache* cache) {
|
||||
size_t const size = samplers.size();
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
} else if (size == 1) {
|
||||
return (uint64_t) samplers[0];
|
||||
}
|
||||
return utils::hash::murmur3((uint32_t*) samplers.data(), samplers.size() * 2, 0);
|
||||
|
||||
utils::FixedCapacityVector<uint32_t> samplerHash;
|
||||
if (size > 0) {
|
||||
samplerHash.reserve(MAX_SAMPLER_COUNT);
|
||||
for (auto sampler : samplers) {
|
||||
samplerHash.push_back(cache->getKey(sampler));
|
||||
}
|
||||
}
|
||||
|
||||
if (size == 1) {
|
||||
return (uint64_t) samplerHash[0];
|
||||
}
|
||||
return utils::hash::murmur3((uint32_t*) samplerHash.data(), samplerHash.size() * 2, 0);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
VulkanDescriptorSetLayoutCache::VulkanDescriptorSetLayoutCache(VkDevice device,
|
||||
fvkmemory::ResourceManager* resourceManager)
|
||||
fvkmemory::ResourceManager* resourceManager, VulkanSamplerCache* cache)
|
||||
: mDevice(device),
|
||||
mResourceManager(resourceManager) {}
|
||||
mResourceManager(resourceManager), mSamplerCache(cache) {}
|
||||
|
||||
VulkanDescriptorSetLayoutCache::~VulkanDescriptorSetLayoutCache() = default;
|
||||
|
||||
@@ -131,7 +142,7 @@ VkDescriptorSetLayout VulkanDescriptorSetLayoutCache::getVkLayout(
|
||||
utils::FixedCapacityVector<VkSampler> immutableSamplers) {
|
||||
LayoutKey key = {
|
||||
.bitmask = bitmasks,
|
||||
.immutableSamplerHash = computeImmutableSamplerHash(immutableSamplers),
|
||||
.immutableSamplerHash = computeImmutableSamplerHash(immutableSamplers, mSamplerCache),
|
||||
};
|
||||
if (auto itr = mVkLayouts.find(key); itr != mVkLayouts.end()) {
|
||||
return itr->second;
|
||||
@@ -153,9 +164,20 @@ VkDescriptorSetLayout VulkanDescriptorSetLayoutCache::getVkLayout(
|
||||
.bindingCount = count,
|
||||
.pBindings = toBind,
|
||||
};
|
||||
LayoutKeyHashFn hashFunc;
|
||||
uint32_t hashedKey = hashFunc(key);
|
||||
utils::FixedCapacityVector<uint32_t> immutableSamplersHashes;
|
||||
immutableSamplersHashes.reserve(MAX_SAMPLER_COUNT);
|
||||
for (auto sampler: immutableSamplers) {
|
||||
immutableSamplersHashes.push_back(mSamplerCache->getKey(sampler));
|
||||
}
|
||||
VulkanDescriptorSetLayoutSerializer setSer(dlinfo, immutableSamplersHashes, hashedKey);
|
||||
|
||||
VkDescriptorSetLayout vklayout;
|
||||
vkCreateDescriptorSetLayout(mDevice, &dlinfo, VKALLOC, &vklayout);
|
||||
mVkLayouts[key] = vklayout;
|
||||
|
||||
mLayoutToKey[vklayout] = hashedKey;
|
||||
return vklayout;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
#include "VulkanHandles.h"
|
||||
|
||||
#include "VulkanSamplerCache.h"
|
||||
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
@@ -35,7 +37,8 @@ namespace filament::backend {
|
||||
|
||||
class VulkanDescriptorSetLayoutCache {
|
||||
public:
|
||||
VulkanDescriptorSetLayoutCache(VkDevice device, fvkmemory::ResourceManager* resourceManager);
|
||||
VulkanDescriptorSetLayoutCache(VkDevice device, fvkmemory::ResourceManager* resourceManager,
|
||||
VulkanSamplerCache* cache);
|
||||
~VulkanDescriptorSetLayoutCache();
|
||||
|
||||
void terminate() noexcept;
|
||||
@@ -48,10 +51,19 @@ public:
|
||||
VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout::Bitmask const& bitmasks,
|
||||
fvkutils::SamplerBitmask externalSamplers,
|
||||
utils::FixedCapacityVector<VkSampler> immutableSamplers = {});
|
||||
uint32_t getKey(VkDescriptorSetLayout layout) {
|
||||
uint32_t key = 0;
|
||||
auto iter = mLayoutToKey.find(layout);
|
||||
if (iter != mLayoutToKey.end()) {
|
||||
key = iter->second;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
private:
|
||||
VkDevice mDevice;
|
||||
fvkmemory::ResourceManager* mResourceManager;
|
||||
VulkanSamplerCache* mSamplerCache;
|
||||
|
||||
struct LayoutKey {
|
||||
// this describes the layout using bitset.
|
||||
@@ -69,6 +81,7 @@ private:
|
||||
};
|
||||
|
||||
tsl::robin_map<LayoutKey, VkDescriptorSetLayout, LayoutKeyHashFn, LayoutKeyEqual> mVkLayouts;
|
||||
std::map<VkDescriptorSetLayout, uint32_t> mLayoutToKey;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
#include "VulkanDescriptorSetLayoutSerializer.h"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <utils/Hash.h>
|
||||
|
||||
namespace filament::backend {
|
||||
VulkanDescriptorSetLayoutSerializer::VulkanDescriptorSetLayoutSerializer(
|
||||
const VkDescriptorSetLayoutCreateInfo& info,
|
||||
utils::FixedCapacityVector<uint32_t> immutableSamplers, uint32_t hash) {
|
||||
std::stringstream filename;
|
||||
filename << "descriptor_set_layout_" << hash << ".json";
|
||||
|
||||
std::ofstream file(filename.str());
|
||||
if (file.is_open()) {
|
||||
std::stringstream buffer;
|
||||
buffer << "[" << std::endl;
|
||||
for (uint32_t i = 0; i < info.bindingCount; ++i) {
|
||||
buffer << "{" << std::endl;
|
||||
buffer << "\"binding\":" << info.pBindings[i].binding << "," << std::endl;
|
||||
buffer << "\"descriptor_type\":" << info.pBindings[i].descriptorType << "," << std::endl;
|
||||
buffer << "\"stage_flags\":" << info.pBindings[i].stageFlags;
|
||||
if (immutableSamplers.size()) {
|
||||
buffer << "," << std::endl;
|
||||
buffer << "\"immutable_samplers\":[" << std::endl;
|
||||
for (uint32_t i = 0; i < immutableSamplers.size(); ++i) {
|
||||
buffer << immutableSamplers[i];
|
||||
if (i < immutableSamplers.size() - 1) {
|
||||
buffer << ",";
|
||||
}
|
||||
}
|
||||
buffer << "]";
|
||||
}
|
||||
buffer << std::endl;
|
||||
buffer << "}";
|
||||
if (i < info.bindingCount - 1) {
|
||||
buffer << ",";
|
||||
}
|
||||
buffer << std::endl;
|
||||
}
|
||||
buffer << "]" << std::endl;
|
||||
|
||||
file << buffer.str();
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
VulkanPipelineLayoutSerializer::VulkanPipelineLayoutSerializer(
|
||||
const VkPipelineLayoutCreateInfo& info, VulkanDescriptorSetLayoutCache* cache,
|
||||
uint32_t hash) {
|
||||
std::stringstream filename;
|
||||
filename << "pipeline_layout_" << hash << ".json";
|
||||
std::ofstream file(filename.str());
|
||||
if (file.is_open()) {
|
||||
std::stringstream buffer;
|
||||
buffer << "{" << std::endl;
|
||||
buffer << "\"flags\":" << info.flags << "," << std::endl;
|
||||
|
||||
buffer << "\"layouts\":[" << std::endl;
|
||||
for (uint32_t i = 0; i < info.setLayoutCount; ++i) {
|
||||
buffer << cache->getKey(info.pSetLayouts[i]);
|
||||
if (i < info.setLayoutCount - 1) {
|
||||
buffer << ",";
|
||||
}
|
||||
}
|
||||
buffer << "]," << std::endl;
|
||||
|
||||
buffer << "\"push_constants\":[" << std::endl;
|
||||
for (uint32_t i = 0; i < info.pushConstantRangeCount; ++i) {
|
||||
buffer << "{" << std::endl;
|
||||
buffer << "\"stage_flags\":" << info.pPushConstantRanges[i].stageFlags << "," << std::endl;
|
||||
buffer << "\"offset\":" << info.pPushConstantRanges[i].offset << ","
|
||||
<< std::endl;
|
||||
buffer << "\"size\":" << info.pPushConstantRanges[i].size
|
||||
<< std::endl;
|
||||
buffer << "}" << std::endl;
|
||||
if (i < info.pushConstantRangeCount - 1) {
|
||||
buffer << ",";
|
||||
}
|
||||
}
|
||||
buffer << "]" << std::endl;
|
||||
|
||||
buffer << "}" << std::endl;
|
||||
file << buffer.str();
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
} // namespace filament::backend
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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_BACKEND_VULKANDESCRIPTORSETLAYOUTSERIALIZER_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANDESCRIPTORSETLAYOUTSERIALIZER_H
|
||||
|
||||
#include "VulkanPipelineLayoutCache.h"
|
||||
#include "VulkanYcbcrConversionCache.h"
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class VulkanDescriptorSetLayoutSerializer {
|
||||
public:
|
||||
VulkanDescriptorSetLayoutSerializer(VulkanDescriptorSetLayoutSerializer const&) = delete;
|
||||
VulkanDescriptorSetLayoutSerializer& operator=(
|
||||
VulkanDescriptorSetLayoutSerializer const&) = delete;
|
||||
|
||||
VulkanDescriptorSetLayoutSerializer(const VkDescriptorSetLayoutCreateInfo& info,
|
||||
utils::FixedCapacityVector<uint32_t> immutableSamplers,
|
||||
uint32_t hash);
|
||||
};
|
||||
|
||||
class VulkanPipelineLayoutSerializer {
|
||||
public:
|
||||
VulkanPipelineLayoutSerializer(VulkanPipelineLayoutSerializer const&) = delete;
|
||||
VulkanPipelineLayoutSerializer& operator=(VulkanPipelineLayoutSerializer const&) = delete;
|
||||
|
||||
VulkanPipelineLayoutSerializer(const VkPipelineLayoutCreateInfo& layout,
|
||||
VulkanDescriptorSetLayoutCache* cache, uint32_t hash);
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_VULKANDESCRIPTORSETLAYOUTSERIALIZER_H
|
||||
@@ -230,16 +230,16 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext& context,
|
||||
mCommands(mPlatform->getDevice(), mPlatform->getGraphicsQueue(),
|
||||
mPlatform->getGraphicsQueueFamilyIndex(), mPlatform->getProtectedGraphicsQueue(),
|
||||
mPlatform->getProtectedGraphicsQueueFamilyIndex(), mContext, &mSemaphoreManager),
|
||||
mPipelineLayoutCache(mPlatform->getDevice()),
|
||||
mPipelineCache(mPlatform->getDevice(), mContext),
|
||||
mPipelineCache(mPlatform->getDevice()),
|
||||
mStagePool(mAllocator, &mResourceManager, &mCommands, &mContext.getPhysicalDeviceLimits()),
|
||||
mBufferCache(mContext, mResourceManager, mAllocator),
|
||||
mFramebufferCache(mPlatform->getDevice()),
|
||||
mYcbcrConversionCache(mPlatform->getDevice()),
|
||||
mSamplerCache(mPlatform->getDevice()),
|
||||
mSamplerCache(mPlatform->getDevice(), &mYcbcrConversionCache),
|
||||
mBlitter(mPlatform->getPhysicalDevice(), &mCommands),
|
||||
mReadPixels(mPlatform->getDevice()),
|
||||
mDescriptorSetLayoutCache(mPlatform->getDevice(), &mResourceManager),
|
||||
mDescriptorSetLayoutCache(mPlatform->getDevice(), &mResourceManager, &mSamplerCache),
|
||||
mPipelineLayoutCache(mPlatform->getDevice(), &mDescriptorSetLayoutCache),
|
||||
mDescriptorSetCache(mPlatform->getDevice(), &mResourceManager),
|
||||
mQueryManager(mPlatform->getDevice()),
|
||||
mExternalImageManager(platform, &mSamplerCache, &mYcbcrConversionCache, &mDescriptorSetCache,
|
||||
@@ -921,6 +921,14 @@ void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
// on Android, disable producer throttling
|
||||
if (mProducerThrottling.isSupported()) {
|
||||
mProducerThrottling.setProducerThrottlingEnabled(
|
||||
static_cast<ANativeWindow*>(nativeWindow), false);
|
||||
}
|
||||
#endif
|
||||
|
||||
auto swapChain = resource_ptr<VulkanSwapChain>::make(&mResourceManager, sch, mPlatform,
|
||||
mContext, &mResourceManager, mAllocator, &mCommands, mStagePool, nativeWindow, flags);
|
||||
swapChain.inc();
|
||||
@@ -1658,7 +1666,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
rpkey.subpassMask = uint8_t(params.subpassMask);
|
||||
|
||||
VkRenderPass renderPass = mFramebufferCache.getRenderPass(rpkey);
|
||||
mPipelineCache.bindRenderPass(renderPass, 0);
|
||||
mPipelineCache.bindRenderPass(renderPass, mFramebufferCache.getRenderPassKey(renderPass), 0);
|
||||
|
||||
// Create the VkFramebuffer or fetch it from cache.
|
||||
VulkanFboCache::FboKey fbkey = rt->getFboKey();
|
||||
@@ -1777,6 +1785,7 @@ void VulkanDriver::nextSubpass(int) {
|
||||
VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
mPipelineCache.bindRenderPass(mCurrentRenderPass.renderPass,
|
||||
mFramebufferCache.getRenderPassKey(mCurrentRenderPass.renderPass),
|
||||
++mCurrentRenderPass.currentSubpass);
|
||||
|
||||
if (mCurrentRenderPass.params.subpassMask & 0x1) {
|
||||
@@ -2152,7 +2161,8 @@ void VulkanDriver::bindPipelineImpl(PipelineState const& pipelineState,
|
||||
mPipelineState.pipelineLayout = pipelineLayout;
|
||||
mPipelineState.descriptorSetMask = descriptorSetMask;
|
||||
|
||||
mPipelineCache.bindLayout(pipelineLayout);
|
||||
auto key = mPipelineLayoutCache.getKey(pipelineLayout);
|
||||
mPipelineCache.bindLayout(pipelineLayout, key);
|
||||
mPipelineCache.bindPipeline(mCurrentRenderPass.commandBuffer);
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,10 @@
|
||||
#include <utils/Allocator.h>
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include "AndroidNativeWindow.h"
|
||||
#endif
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class VulkanPlatform;
|
||||
@@ -144,7 +148,6 @@ private:
|
||||
|
||||
VulkanSemaphoreManager mSemaphoreManager;
|
||||
VulkanCommands mCommands;
|
||||
VulkanPipelineLayoutCache mPipelineLayoutCache;
|
||||
VulkanPipelineCache mPipelineCache;
|
||||
VulkanStagePool mStagePool;
|
||||
VulkanBufferCache mBufferCache;
|
||||
@@ -154,6 +157,7 @@ private:
|
||||
VulkanBlitter mBlitter;
|
||||
VulkanReadPixels mReadPixels;
|
||||
VulkanDescriptorSetLayoutCache mDescriptorSetLayoutCache;
|
||||
VulkanPipelineLayoutCache mPipelineLayoutCache;
|
||||
VulkanDescriptorSetCache mDescriptorSetCache;
|
||||
VulkanQueryManager mQueryManager;
|
||||
VulkanExternalImageManager mExternalImageManager;
|
||||
@@ -202,6 +206,9 @@ private:
|
||||
bool const mIsSRGBSwapChainSupported;
|
||||
bool const mIsMSAASwapChainSupported;
|
||||
backend::StereoscopicType const mStereoscopicType;
|
||||
#if defined(__ANDROID__)
|
||||
AndroidProducerThrottling mProducerThrottling;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include "VulkanFboCache.h"
|
||||
#include "VulkanRenderPassStateSerializer.h"
|
||||
|
||||
#include "VulkanConstants.h"
|
||||
#include "vulkan/utils/Image.h"
|
||||
@@ -69,6 +70,15 @@ VulkanFboCache::~VulkanFboCache() {
|
||||
<< "Please explicitly call terminate() while the VkDevice is still alive.";
|
||||
}
|
||||
|
||||
uint32_t VulkanFboCache::getRenderPassKey(VkRenderPass pass) noexcept {
|
||||
uint32_t key = 0;
|
||||
auto iter = mRenderPassToKey.find(pass);
|
||||
if (iter != mRenderPassToKey.end()) {
|
||||
key = iter->second;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
VkFramebuffer VulkanFboCache::getFramebuffer(FboKey const& config) noexcept {
|
||||
FboMap::iterator iter = mFramebufferCache.find(config);
|
||||
if (UTILS_LIKELY(iter != mFramebufferCache.end() && iter->second.handle != VK_NULL_HANDLE)) {
|
||||
@@ -117,6 +127,7 @@ VkFramebuffer VulkanFboCache::getFramebuffer(FboKey const& config) noexcept {
|
||||
VkResult error = vkCreateFramebuffer(mDevice, &info, VKALLOC, &framebuffer);
|
||||
FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create framebuffer."
|
||||
<< " error=" << static_cast<int32_t>(error);
|
||||
|
||||
mFramebufferCache[config] = {framebuffer, mCurrentTime};
|
||||
return framebuffer;
|
||||
}
|
||||
@@ -326,6 +337,12 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey const& config) noexcept
|
||||
VkResult error = vkCreateRenderPass(mDevice, &renderPassInfo, VKALLOC, &renderPass);
|
||||
FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create render pass."
|
||||
<< " error=" << error;
|
||||
|
||||
RenderPassHash hashFunc;
|
||||
uint32_t key = hashFunc(config);
|
||||
VulkanRenderPassStateSerializer passSer(renderPassInfo, key);
|
||||
mRenderPassToKey[renderPass] = key;
|
||||
|
||||
mRenderPassCache[config] = {renderPass, mCurrentTime};
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_FBO_CACHE)
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
#include <map>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -97,6 +98,8 @@ public:
|
||||
explicit VulkanFboCache(VkDevice device);
|
||||
~VulkanFboCache();
|
||||
|
||||
uint32_t getRenderPassKey(VkRenderPass pass) noexcept;
|
||||
|
||||
// Retrieves or creates a VkFramebuffer handle.
|
||||
VkFramebuffer getFramebuffer(FboKey const& config) noexcept;
|
||||
|
||||
@@ -118,6 +121,7 @@ private:
|
||||
FboMap mFramebufferCache;
|
||||
|
||||
tsl::robin_map<RenderPassKey, RenderPassVal, RenderPassHash, RenderPassEq> mRenderPassCache;
|
||||
std::map<VkRenderPass, uint32_t> mRenderPassToKey;
|
||||
tsl::robin_map<VkRenderPass, uint32_t> mRenderPassRefCount;
|
||||
uint32_t mCurrentTime = 0;
|
||||
};
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <utils/compiler.h> // UTILS_FALLTHROUGH
|
||||
#include <utils/Panic.h> // ASSERT_POSTCONDITION
|
||||
#include <utils/CString.h>
|
||||
#include <fstream>
|
||||
|
||||
using namespace bluevk;
|
||||
|
||||
@@ -179,6 +180,23 @@ VulkanAttachment createSwapchainAttachment(const fvkmemory::resource_ptr<VulkanT
|
||||
};
|
||||
}
|
||||
|
||||
static inline uint64_t serializeShaderModule(utils::CString progName, VkShaderModuleCreateInfo info,
|
||||
VkShaderStageFlagBits stage) {
|
||||
std::stringstream filename;
|
||||
filename << progName.c_str() << "_" << stage << "_";
|
||||
constexpr uint32_t wordReadCount = 8;
|
||||
uint64_t key = utils::hash::murmur3(info.pCode, wordReadCount, 0);
|
||||
key |= uint64_t(utils::hash::murmur3(info.pCode + (wordReadCount / 2), wordReadCount, 0)) << 32;
|
||||
filename << key << ".bin";
|
||||
|
||||
std::ofstream file(filename.str());
|
||||
if (file.is_open()) {
|
||||
file.write(reinterpret_cast<const char*>(info.pCode), info.codeSize);
|
||||
}
|
||||
file.close();
|
||||
return key;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(DescriptorSetLayout&& layout,
|
||||
@@ -280,6 +298,12 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
|
||||
.codeSize = dataSize,
|
||||
.pCode = data,
|
||||
};
|
||||
if (i == 0) {
|
||||
mVertexShaderHash = serializeShaderModule(name, moduleInfo, VK_SHADER_STAGE_VERTEX_BIT);
|
||||
} else if (i == 1) {
|
||||
mFragmentShaderHash =
|
||||
serializeShaderModule(name, moduleInfo, VK_SHADER_STAGE_FRAGMENT_BIT);
|
||||
}
|
||||
VkResult result = vkCreateShaderModule(mDevice, &moduleInfo, VKALLOC, &module);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Unable to create shader module."
|
||||
|
||||
@@ -65,6 +65,8 @@ struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Reso
|
||||
|
||||
using DescriptorSetLayoutArray = std::array<VkDescriptorSetLayout,
|
||||
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
|
||||
using DescriptorSetLayoutHashArray = std::array<uint64_t,
|
||||
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
|
||||
|
||||
// The bitmask representation of a set layout.
|
||||
struct Bitmask {
|
||||
@@ -280,6 +282,10 @@ struct VulkanProgram : public HwProgram, fvkmemory::Resource {
|
||||
mInfo->pushConstantDescription.write(cmdbuf, layout, stage, index, value);
|
||||
}
|
||||
|
||||
inline uint64_t getVertexShaderHash() { return mVertexShaderHash;
|
||||
}
|
||||
inline uint64_t getFragmentShaderHash() { return mFragmentShaderHash; }
|
||||
|
||||
// TODO: handle compute shaders.
|
||||
// The expected order of shaders - from frontend to backend - is vertex, fragment, compute.
|
||||
static constexpr uint8_t const MAX_SHADER_MODULES = 2;
|
||||
@@ -295,6 +301,8 @@ private:
|
||||
};
|
||||
|
||||
PipelineInfo* mInfo;
|
||||
uint64_t mVertexShaderHash;
|
||||
uint64_t mFragmentShaderHash;
|
||||
VkDevice mDevice = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include "VulkanPipelineCache.h"
|
||||
#include "VulkanPipelineStateSerializer.h"
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Panic.h>
|
||||
@@ -34,49 +35,17 @@ using namespace bluevk;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
|
||||
void printPipelineFeedbackInfo(VkPipelineCreationFeedbackCreateInfo const& feedbackInfo) {
|
||||
VkPipelineCreationFeedback const& pipelineInfo = *feedbackInfo.pPipelineCreationFeedback;
|
||||
if (!(pipelineInfo.flags & VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool const isCacheHit =
|
||||
(pipelineInfo.flags & VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT);
|
||||
FVK_LOGD << "Pipeline build stats - Cache hit: " << isCacheHit
|
||||
<< ", Time: " << pipelineInfo.duration / 1000000.0 << "ms";
|
||||
|
||||
for (uint32_t i = 0; i < feedbackInfo.pipelineStageCreationFeedbackCount; ++i) {
|
||||
VkPipelineCreationFeedback const& stageInfo = feedbackInfo.pPipelineStageCreationFeedbacks[i];
|
||||
if (!(stageInfo.flags & VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool const isVertexShader = (i == 0);
|
||||
bool const isCacheHit = (stageInfo.flags &
|
||||
VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT);
|
||||
FVK_LOGD << (isVertexShader ? "Vertex" : "Fragment")
|
||||
<< " shader build stats - Cache hit: " << isCacheHit
|
||||
<< ", Time: " << stageInfo.duration / 1000000.0 << "ms";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
VulkanPipelineCache::VulkanPipelineCache(VkDevice device, VulkanContext const& context)
|
||||
: mDevice(device),
|
||||
mContext(context) {
|
||||
VulkanPipelineCache::VulkanPipelineCache(VkDevice device)
|
||||
: mDevice(device) {
|
||||
VkPipelineCacheCreateInfo createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
|
||||
};
|
||||
bluevk::vkCreatePipelineCache(mDevice, &createInfo, VKALLOC, &mPipelineCache);
|
||||
}
|
||||
|
||||
void VulkanPipelineCache::bindLayout(VkPipelineLayout layout) noexcept {
|
||||
void VulkanPipelineCache::bindLayout(VkPipelineLayout layout, uint32_t key) noexcept {
|
||||
mPipelineRequirements.layout = layout;
|
||||
mPipelineLayoutKey = key;
|
||||
}
|
||||
|
||||
VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::getOrCreatePipeline() noexcept {
|
||||
@@ -110,6 +79,7 @@ void VulkanPipelineCache::bindPipeline(VulkanCommandBuffer* commands) {
|
||||
VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() noexcept {
|
||||
assert_invariant(mPipelineRequirements.shaders[0] && "Vertex shader is not bound.");
|
||||
assert_invariant(mPipelineRequirements.layout && "No pipeline layout specified");
|
||||
VulkanPipelineStateSerializer serializer(mProgramName);
|
||||
|
||||
VkPipelineShaderStageCreateInfo shaderStages[SHADER_MODULE_COUNT];
|
||||
shaderStages[0] = {
|
||||
@@ -122,6 +92,12 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
||||
shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
shaderStages[1].module = mPipelineRequirements.shaders[1];
|
||||
|
||||
VulkanPipelineStateSerializer::ShaderStageInfo vtx = { shaderStages[0].stage, mVertexShaderHash,
|
||||
shaderStages[0].pName };
|
||||
VulkanPipelineStateSerializer::ShaderStageInfo frg = { shaderStages[1].stage, mFragmentShaderHash,
|
||||
shaderStages[1].pName };
|
||||
serializer << vtx << frg;
|
||||
|
||||
bool const hasFragmentShader = shaderStages[1].module != VK_NULL_HANDLE;
|
||||
|
||||
VkPipelineColorBlendAttachmentState colorBlendAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT];
|
||||
@@ -140,9 +116,11 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
||||
for (uint32_t i = 0; i < VERTEX_ATTRIBUTE_COUNT; i++) {
|
||||
if (mPipelineRequirements.vertexAttributes[i].format > 0) {
|
||||
vertexAttributes[numVertexAttribs++] = mPipelineRequirements.vertexAttributes[i];
|
||||
serializer << mPipelineRequirements.vertexAttributes[i];
|
||||
}
|
||||
if (mPipelineRequirements.vertexBuffers[i].stride > 0) {
|
||||
vertexBuffers[numVertexBuffers++] = mPipelineRequirements.vertexBuffers[i];
|
||||
serializer << mPipelineRequirements.vertexBuffers[i];
|
||||
}
|
||||
}
|
||||
VkPipelineVertexInputStateCreateInfo vertexInputState = {
|
||||
@@ -156,11 +134,15 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
||||
.topology = (VkPrimitiveTopology) mPipelineRequirements.topology,
|
||||
};
|
||||
serializer << inputAssemblyState;
|
||||
|
||||
VkPipelineViewportStateCreateInfo viewportState = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.viewportCount = 1,
|
||||
.scissorCount = 1,
|
||||
};
|
||||
serializer << viewportState;
|
||||
|
||||
VkDynamicState dynamicStateEnables[] = {
|
||||
VK_DYNAMIC_STATE_VIEWPORT,
|
||||
VK_DYNAMIC_STATE_SCISSOR,
|
||||
@@ -170,6 +152,8 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
||||
.dynamicStateCount = 2,
|
||||
.pDynamicStates = dynamicStateEnables,
|
||||
};
|
||||
serializer << dynamicState;
|
||||
|
||||
auto const& raster = mPipelineRequirements.rasterState;
|
||||
VkPipelineRasterizationStateCreateInfo vkRaster = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
@@ -183,6 +167,8 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
||||
.depthBiasSlopeFactor = raster.depthBiasSlopeFactor,
|
||||
.lineWidth = 1.0f,
|
||||
};
|
||||
serializer << vkRaster;
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo vkMs = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.rasterizationSamples = (VkSampleCountFlagBits) raster.rasterizationSamples,
|
||||
@@ -191,6 +177,8 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
||||
.alphaToCoverageEnable = raster.alphaToCoverageEnable,
|
||||
.alphaToOneEnable = VK_FALSE,
|
||||
};
|
||||
serializer << vkMs;
|
||||
|
||||
VkPipelineDepthStencilStateCreateInfo vkDs = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
||||
.depthTestEnable = VK_TRUE,
|
||||
@@ -210,6 +198,12 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
||||
.writeMask = 0u,
|
||||
.reference = 0u,
|
||||
};
|
||||
serializer << vkDs;
|
||||
|
||||
//layout
|
||||
serializer.setPipelineLayoutKey(mPipelineLayoutKey);
|
||||
// renderPass
|
||||
serializer.setRenderPassKey(mRenderPassKey);
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipelineCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
@@ -248,52 +242,36 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
||||
colorBlendAttachments[i] = colorBlendAttachments[0];
|
||||
}
|
||||
}
|
||||
serializer << colorBlendState;
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
|
||||
FVK_LOGD << "vkCreateGraphicsPipelines with shaders = (" << shaderStages[0].module << ", "
|
||||
<< shaderStages[1].module << ")";
|
||||
|
||||
VkPipelineCreationFeedback stageFeedbacks[SHADER_MODULE_COUNT] = {};
|
||||
VkPipelineCreationFeedback pipelineFeedback = {};
|
||||
VkPipelineCreationFeedbackCreateInfo feedbackInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_CREATION_FEEDBACK_CREATE_INFO_EXT,
|
||||
.pNext = nullptr,
|
||||
.pPipelineCreationFeedback = &pipelineFeedback,
|
||||
.pipelineStageCreationFeedbackCount = hasFragmentShader ? SHADER_MODULE_COUNT : 1,
|
||||
.pPipelineStageCreationFeedbacks = stageFeedbacks,
|
||||
};
|
||||
|
||||
if (mContext.pipelineCreationFeedbackSupported()) {
|
||||
feedbackInfo.pNext = pipelineCreateInfo.pNext;
|
||||
pipelineCreateInfo.pNext = &feedbackInfo;
|
||||
}
|
||||
#endif
|
||||
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
|
||||
FVK_LOGD << "vkCreateGraphicsPipelines with shaders = ("
|
||||
<< shaderStages[0].module << ", " << shaderStages[1].module << ")";
|
||||
#endif
|
||||
PipelineCacheEntry cacheEntry = {
|
||||
.lastUsed = mCurrentTime,
|
||||
};
|
||||
VkResult error = vkCreateGraphicsPipelines(mDevice, mPipelineCache, 1, &pipelineCreateInfo,
|
||||
VKALLOC, &cacheEntry.handle);
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
|
||||
FVK_LOGD << "vkCreateGraphicsPipelines with shaders = (" << shaderStages[0].module << ", "
|
||||
<< shaderStages[1].module << ")";
|
||||
|
||||
if (mContext.pipelineCreationFeedbackSupported()) {
|
||||
printPipelineFeedbackInfo(feedbackInfo);
|
||||
}
|
||||
#endif
|
||||
|
||||
assert_invariant(error == VK_SUCCESS);
|
||||
if (error != VK_SUCCESS) {
|
||||
FVK_LOGE << "vkCreateGraphicsPipelines error " << error;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PipelineHashFn hashFunc;
|
||||
|
||||
serializer.setID(hashFunc(mPipelineRequirements));
|
||||
return &mPipelines.emplace(mPipelineRequirements, cacheEntry).first.value();
|
||||
}
|
||||
|
||||
void VulkanPipelineCache::bindProgram(fvkmemory::resource_ptr<VulkanProgram> program) noexcept {
|
||||
mPipelineRequirements.shaders[0] = program->getVertexShader();
|
||||
mPipelineRequirements.shaders[1] = program->getFragmentShader();
|
||||
mProgramName = program->name;
|
||||
mVertexShaderHash = program->getVertexShaderHash();
|
||||
mFragmentShaderHash= program->getFragmentShaderHash();
|
||||
|
||||
|
||||
// If this is a debug build, validate the current shader.
|
||||
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
|
||||
@@ -308,9 +286,10 @@ void VulkanPipelineCache::bindRasterState(RasterState const& rasterState) noexce
|
||||
mPipelineRequirements.rasterState = rasterState;
|
||||
}
|
||||
|
||||
void VulkanPipelineCache::bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept {
|
||||
void VulkanPipelineCache::bindRenderPass(VkRenderPass renderPass, uint32_t passKey, int subpassIndex) noexcept {
|
||||
mPipelineRequirements.renderPass = renderPass;
|
||||
mPipelineRequirements.subpassIndex = subpassIndex;
|
||||
mRenderPassKey = passKey;
|
||||
}
|
||||
|
||||
void VulkanPipelineCache::bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept {
|
||||
|
||||
@@ -86,9 +86,9 @@ public:
|
||||
|
||||
static_assert(sizeof(RasterState) == 16, "RasterState must not have implicit padding.");
|
||||
|
||||
VulkanPipelineCache(VkDevice device, VulkanContext const& context);
|
||||
VulkanPipelineCache(VkDevice device);
|
||||
|
||||
void bindLayout(VkPipelineLayout layout) noexcept;
|
||||
void bindLayout(VkPipelineLayout layout, uint32_t key) noexcept;
|
||||
|
||||
// Creates a new pipeline if necessary and binds it using vkCmdBindPipeline.
|
||||
void bindPipeline(VulkanCommandBuffer* commands);
|
||||
@@ -96,7 +96,7 @@ public:
|
||||
// Each of the following methods are fast and do not make Vulkan calls.
|
||||
void bindProgram(fvkmemory::resource_ptr<VulkanProgram> program) noexcept;
|
||||
void bindRasterState(RasterState const& rasterState) noexcept;
|
||||
void bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept;
|
||||
void bindRenderPass(VkRenderPass renderPass, uint32_t passKey, int subpassIndex) noexcept;
|
||||
void bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept;
|
||||
void bindVertexArray(VkVertexInputAttributeDescription const* attribDesc,
|
||||
VkVertexInputBindingDescription const* bufferDesc, uint8_t count);
|
||||
@@ -207,11 +207,14 @@ private:
|
||||
|
||||
// Current requirements for the pipeline layout, pipeline, and descriptor sets.
|
||||
PipelineKey mPipelineRequirements = {};
|
||||
utils::CString mProgramName;
|
||||
uint64_t mVertexShaderHash;
|
||||
uint64_t mFragmentShaderHash;
|
||||
uint32_t mPipelineLayoutKey;
|
||||
uint32_t mRenderPassKey;
|
||||
|
||||
// Current bindings for the pipeline and descriptor sets.
|
||||
PipelineKey mBoundPipeline = {};
|
||||
|
||||
[[maybe_unused]] VulkanContext const& mContext;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include "VulkanPipelineLayoutCache.h"
|
||||
#include "VulkanDescriptorSetLayoutSerializer.h"
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -23,7 +24,11 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
|
||||
fvkmemory::resource_ptr<VulkanProgram> program) {
|
||||
PipelineLayoutKey key = {};
|
||||
uint8_t descSetLayoutCount = 0;
|
||||
key.descSetLayouts = descriptorSetLayouts;
|
||||
DescriptorSetLayoutHashArray hashArray;
|
||||
for (uint32_t i = 0; i < descriptorSetLayouts.size(); ++i) {
|
||||
hashArray[i] = mDescriptorSetCache->getKey(descriptorSetLayouts[i]);
|
||||
}
|
||||
key.descSetLayouts = hashArray;
|
||||
for (auto layoutHandle: descriptorSetLayouts) {
|
||||
if (layoutHandle == VK_NULL_HANDLE) {
|
||||
break;
|
||||
@@ -62,11 +67,14 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.setLayoutCount = (uint32_t) descSetLayoutCount,
|
||||
.pSetLayouts = key.descSetLayouts.data(),
|
||||
.pSetLayouts = descriptorSetLayouts.data(),
|
||||
.pushConstantRangeCount = pushConstantRangeCount,
|
||||
.pPushConstantRanges = pushConstantRanges,
|
||||
};
|
||||
|
||||
PipelineLayoutKeyHashFn hashFunc;
|
||||
uint32_t hashedKey = hashFunc(key);
|
||||
VulkanPipelineLayoutSerializer pipelineSer(info, mDescriptorSetCache, hashedKey);
|
||||
VkPipelineLayout layout;
|
||||
vkCreatePipelineLayout(mDevice, &info, VKALLOC, &layout);
|
||||
|
||||
@@ -74,6 +82,8 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
|
||||
.handle = layout,
|
||||
.lastUsed = mTimestamp++,
|
||||
};
|
||||
|
||||
mPipelineToKey[layout] = hashedKey;
|
||||
return layout;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#define TNT_FILAMENT_BACKEND_VULKANPIPELINELAYOUTCACHE_H
|
||||
|
||||
#include "VulkanHandles.h"
|
||||
#include "VulkanDescriptorSetLayoutCache.h"
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
|
||||
@@ -30,10 +31,11 @@ namespace filament::backend {
|
||||
class VulkanPipelineLayoutCache {
|
||||
public:
|
||||
using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
|
||||
using DescriptorSetLayoutHashArray = VulkanDescriptorSetLayout::DescriptorSetLayoutHashArray;
|
||||
|
||||
VulkanPipelineLayoutCache(VkDevice device)
|
||||
VulkanPipelineLayoutCache(VkDevice device, VulkanDescriptorSetLayoutCache* cache)
|
||||
: mDevice(device),
|
||||
mTimestamp(0) {}
|
||||
mTimestamp(0), mDescriptorSetCache(cache) {}
|
||||
|
||||
void terminate() noexcept;
|
||||
|
||||
@@ -45,7 +47,7 @@ public:
|
||||
};
|
||||
|
||||
struct PipelineLayoutKey {
|
||||
DescriptorSetLayoutArray descSetLayouts = {}; // 8 * 4
|
||||
DescriptorSetLayoutHashArray descSetLayouts = {}; // 8 * 4
|
||||
PushConstantKey pushConstant[Program::SHADER_TYPE_COUNT] = {}; // 2 * 3
|
||||
uint16_t padding = 0;
|
||||
};
|
||||
@@ -58,6 +60,14 @@ public:
|
||||
// are described in the program.
|
||||
VkPipelineLayout getLayout(DescriptorSetLayoutArray const& descriptorSetLayouts,
|
||||
fvkmemory::resource_ptr<VulkanProgram> program);
|
||||
uint32_t getKey(VkPipelineLayout layout) {
|
||||
uint32_t key = 0;
|
||||
auto iter = mPipelineToKey.find(layout);
|
||||
if (iter != mPipelineToKey.end()) {
|
||||
key = iter->second;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
private:
|
||||
using Timestamp = uint64_t;
|
||||
@@ -79,6 +89,8 @@ private:
|
||||
VkDevice mDevice;
|
||||
Timestamp mTimestamp;
|
||||
PipelineLayoutMap mPipelineLayouts;
|
||||
std::map<VkPipelineLayout, uint32_t> mPipelineToKey;
|
||||
VulkanDescriptorSetLayoutCache* mDescriptorSetCache;
|
||||
};
|
||||
|
||||
} // filament::backend
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 <fstream>
|
||||
|
||||
#include "VulkanPipelineStateSerializer.h"
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include "VulkanConstants.h"
|
||||
#include "VulkanHandles.h"
|
||||
|
||||
#if defined(__clang__)
|
||||
// Vulkan functions often immediately dereference pointers, so it's fine to pass in a pointer
|
||||
// to a stack-allocated variable.
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wreturn-stack-address"
|
||||
#endif
|
||||
|
||||
using namespace bluevk;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
VulkanPipelineStateSerializer::VulkanPipelineStateSerializer(const utils::CString& name) {
|
||||
mBuffer << "{" << std::endl;
|
||||
mBuffer << "\"program_name\":" << "\"" << name.c_str() << "\"" << "," << std::endl;
|
||||
}
|
||||
VulkanPipelineStateSerializer::~VulkanPipelineStateSerializer() {
|
||||
//Shader Modules
|
||||
mBuffer << "\"shader_modules\":[" << std::endl;
|
||||
for (uint32_t i = 0; i < mShaderStages.size(); ++i) {
|
||||
mBuffer << "{" << std::endl;
|
||||
mBuffer << "\"stage\":" << mShaderStages[i].stage << "," << std::endl;
|
||||
mBuffer << "\"key\":" << mShaderStages[i].module << "," << std::endl;
|
||||
mBuffer << "\"name\":" << "\"" << mShaderStages[i].mName << "\"" << std::endl;
|
||||
mBuffer << "}";
|
||||
if (i < mShaderStages.size() - 1) {
|
||||
mBuffer << ",";
|
||||
}
|
||||
mBuffer << std::endl;
|
||||
}
|
||||
mBuffer << "]," << std::endl;
|
||||
|
||||
//Vertex Attribute
|
||||
mBuffer << "\"vertex_attributes\":[" << std::endl;
|
||||
for (uint32_t i = 0; i < mVtxAttribs.size(); ++i) {
|
||||
mBuffer << "{" << std::endl;
|
||||
mBuffer << "\"location\":" << mVtxAttribs[i].location << "," << std::endl;
|
||||
mBuffer << "\"binding\":" << mVtxAttribs[i].binding << "," << std::endl;
|
||||
mBuffer << "\"format\":" << mVtxAttribs[i].format << "," << std::endl;
|
||||
mBuffer << "\"offset\":" << mVtxAttribs[i].offset << std::endl;
|
||||
mBuffer << "}";
|
||||
if (i < mVtxAttribs.size() - 1) {
|
||||
mBuffer << ",";
|
||||
}
|
||||
mBuffer << std::endl;
|
||||
}
|
||||
mBuffer << "]," << std::endl;
|
||||
|
||||
// Vertex Bindings
|
||||
mBuffer << "\"vertex_bindings\":[" << std::endl;
|
||||
for (uint32_t i = 0; i < mVtxBindings.size(); ++i) {
|
||||
mBuffer << "{" << std::endl;
|
||||
mBuffer << "\"binding\":" << mVtxBindings[i].binding << "," << std::endl;
|
||||
mBuffer << "\"stride\":" << mVtxBindings[i].stride << "," << std::endl;
|
||||
mBuffer << "\"input_rate\":" << mVtxBindings[i].inputRate << std::endl;
|
||||
mBuffer << "}";
|
||||
if (i < mVtxBindings.size() - 1) {
|
||||
mBuffer << ",";
|
||||
}
|
||||
mBuffer << std::endl;
|
||||
}
|
||||
mBuffer << "]" << std::endl;
|
||||
|
||||
mBuffer << "}" << std::endl;
|
||||
|
||||
std::ofstream file(mFileName);
|
||||
if (file.is_open()) {
|
||||
file << mBuffer.str();
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
void VulkanPipelineStateSerializer::setPipelineLayoutKey(uint32_t pipelineLayoutKey) {
|
||||
mBuffer << "\"layout\":" << pipelineLayoutKey << "," << std::endl;
|
||||
}
|
||||
|
||||
void VulkanPipelineStateSerializer::setRenderPassKey(uint32_t renderPassKey) {
|
||||
mBuffer << "\"render_pass\":" << renderPassKey << "," << std::endl;
|
||||
}
|
||||
|
||||
void VulkanPipelineStateSerializer::setID(uint32_t key) {
|
||||
std::stringstream temp;
|
||||
temp << "pipeline_" << key << ".json";
|
||||
temp >> mFileName;
|
||||
}
|
||||
|
||||
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
|
||||
const VkPipelineInputAssemblyStateCreateInfo& inputAsm) {
|
||||
mBuffer << "\"topology\":" << inputAsm.topology << "," << std::endl;
|
||||
return *this;
|
||||
}
|
||||
|
||||
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
|
||||
const VkPipelineViewportStateCreateInfo& viewport) {
|
||||
mBuffer << "\"viewport_count\":" << viewport.viewportCount << "," << std::endl;
|
||||
mBuffer << "\"scissor_count\":" << viewport.scissorCount << "," << std::endl;
|
||||
return *this;
|
||||
}
|
||||
|
||||
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
|
||||
const VkPipelineRasterizationStateCreateInfo& rasterState) {
|
||||
mBuffer << "\"raster_states\":{" << std::endl;
|
||||
mBuffer << "\"depth_clamp_enable\":" << rasterState.depthClampEnable << "," << std::endl;
|
||||
mBuffer << "\"polygon_mode\":" << rasterState.polygonMode << "," << std::endl;
|
||||
mBuffer << "\"cull_mode\":" << rasterState.cullMode << "," << std::endl;
|
||||
mBuffer << "\"front_face\":" << rasterState.frontFace << "," << std::endl;
|
||||
mBuffer << "\"depth_bias_enable\":" << rasterState.depthBiasEnable << "," << std::endl;
|
||||
mBuffer << "\"depth_bias_constant_factor\":" << rasterState.depthBiasConstantFactor << ","
|
||||
<< std::endl;
|
||||
mBuffer << "\"depth_bias_clamp\":" << rasterState.depthBiasClamp << "," << std::endl;
|
||||
mBuffer << "\"depth_bias_slope_factor\":" << rasterState.depthBiasSlopeFactor << "," << std::endl;
|
||||
mBuffer << "\"line_width\":" << rasterState.lineWidth << std::endl;
|
||||
mBuffer << "}," << std::endl;
|
||||
return *this;
|
||||
}
|
||||
|
||||
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
|
||||
const VkPipelineDepthStencilStateCreateInfo& depthStencil) {
|
||||
mBuffer << "\"depth_stencil_states\":{" << std::endl;
|
||||
mBuffer << "\"depth_test_enable\":" << depthStencil.depthTestEnable << "," << std::endl;
|
||||
mBuffer << "\"depth_write_enable\":" << depthStencil.depthWriteEnable << "," << std::endl;
|
||||
mBuffer << "\"depth_compare_op\":" << depthStencil.depthCompareOp << "," << std::endl;
|
||||
mBuffer << "\"depth_bounds_test_enable\":" << depthStencil.depthBoundsTestEnable << ","
|
||||
<< std::endl;
|
||||
mBuffer << "\"min_depth_bounds\":" << depthStencil.minDepthBounds << "," << std::endl;
|
||||
mBuffer << "\"max_depth_bounds\":" << depthStencil.maxDepthBounds << "," << std::endl;
|
||||
mBuffer << "\"front\":{" << std::endl;
|
||||
mBuffer << "\"fail_op\":" << depthStencil.front.failOp << "," << std::endl;
|
||||
mBuffer << "\"pass_op\":" << depthStencil.front.passOp << "," << std::endl;
|
||||
mBuffer << "\"depth_fail_op\":" << depthStencil.front.depthFailOp << "," << std::endl;
|
||||
mBuffer << "\"compare_op\":" << depthStencil.front.compareOp << "," << std::endl;
|
||||
mBuffer << "\"compare_mask\":" << depthStencil.front.compareMask << "," << std::endl;
|
||||
mBuffer << "\"write_mask\":" << depthStencil.front.writeMask << "," << std::endl;
|
||||
mBuffer << "\"reference\":" << depthStencil.front.reference << std::endl;
|
||||
mBuffer << "}," << std::endl;
|
||||
mBuffer << "\"back\":{" << std::endl;
|
||||
mBuffer << "\"fail_op\":" << depthStencil.back.failOp << "," << std::endl;
|
||||
mBuffer << "\"pass_op\":" << depthStencil.back.passOp << "," << std::endl;
|
||||
mBuffer << "\"depth_fail_op\":" << depthStencil.back.depthFailOp << "," << std::endl;
|
||||
mBuffer << "\"compare_op\":" << depthStencil.back.compareOp << "," << std::endl;
|
||||
mBuffer << "\"compare_mask\":" << depthStencil.back.compareMask << "," << std::endl;
|
||||
mBuffer << "\"write_mask\":" << depthStencil.back.writeMask << "," << std::endl;
|
||||
mBuffer << "\"reference\":" << depthStencil.back.reference << std::endl;
|
||||
mBuffer << "}" << std::endl;
|
||||
mBuffer << "}," << std::endl;
|
||||
return *this;
|
||||
}
|
||||
|
||||
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
|
||||
const VkPipelineMultisampleStateCreateInfo& multiSample) {
|
||||
mBuffer << "\"multisample_states\":{" << std::endl;
|
||||
mBuffer << "\"rasterization_samples\":" << multiSample.rasterizationSamples << "," << std::endl;
|
||||
mBuffer << "\"sample_shading_enable\":" << multiSample.sampleShadingEnable << "," << std::endl;
|
||||
mBuffer << "\"min_sample_shading\":" << multiSample.minSampleShading << "," << std::endl;
|
||||
mBuffer << "\"alpha_to_coverage_enable\":" << multiSample.alphaToCoverageEnable << ","
|
||||
<< std::endl;
|
||||
mBuffer << "\"alpha_to_one_enable\":" << multiSample.alphaToOneEnable << std::endl;
|
||||
mBuffer << "}," << std::endl;
|
||||
return *this;
|
||||
}
|
||||
|
||||
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
|
||||
const VkPipelineDynamicStateCreateInfo& dyStates) {
|
||||
mBuffer << "\"dynamic_states\":[" << std::endl;
|
||||
for (uint32_t i = 0; i < dyStates.dynamicStateCount; ++i) {
|
||||
mBuffer << dyStates.pDynamicStates[i];
|
||||
if (i < dyStates.dynamicStateCount - 1) {
|
||||
mBuffer << ",";
|
||||
}
|
||||
mBuffer << std::endl;
|
||||
}
|
||||
mBuffer << "]," << std::endl;
|
||||
return *this;
|
||||
}
|
||||
|
||||
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
|
||||
const VkPipelineColorBlendStateCreateInfo& blendStateInfo) {
|
||||
mBuffer << "\"color_blend_state_attachments\":[" << std::endl;
|
||||
for (uint32_t i = 0; i < blendStateInfo.attachmentCount; ++i) {
|
||||
mBuffer << "{" << std::endl;
|
||||
mBuffer << "\"blend_enable\":" << blendStateInfo.pAttachments[i].blendEnable << ","
|
||||
<< std::endl;
|
||||
mBuffer << "\"src_color_blend_factor\":" << blendStateInfo.pAttachments[i].srcColorBlendFactor
|
||||
<< ","
|
||||
<< std::endl;
|
||||
mBuffer << "\"dst_color_blend_factor\":" << blendStateInfo.pAttachments[i].dstColorBlendFactor
|
||||
<< ","
|
||||
<< std::endl;
|
||||
mBuffer << "\"color_blend_op\":" << blendStateInfo.pAttachments[i].colorBlendOp << ","
|
||||
<< std::endl;
|
||||
mBuffer << "\"src_alpha_blend_factor\":" << blendStateInfo.pAttachments[i].srcAlphaBlendFactor
|
||||
<< ","
|
||||
<< std::endl;
|
||||
mBuffer << "\"dst_alpha_blend_factor\":" << blendStateInfo.pAttachments[i].dstAlphaBlendFactor
|
||||
<< ","
|
||||
<< std::endl;
|
||||
mBuffer << "\"alpha_blend_op\":" << blendStateInfo.pAttachments[i].alphaBlendOp << ","
|
||||
<< std::endl;
|
||||
mBuffer << "\"color_write_mask\":" << blendStateInfo.pAttachments[i].colorWriteMask
|
||||
<< std::endl;
|
||||
mBuffer << "}";
|
||||
if (i < blendStateInfo.attachmentCount - 1) {
|
||||
mBuffer << ",";
|
||||
}
|
||||
mBuffer << std::endl;
|
||||
}
|
||||
mBuffer << "]," << std::endl;
|
||||
return *this;
|
||||
}
|
||||
|
||||
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
|
||||
const ShaderStageInfo& shaderStages) {
|
||||
mShaderStages.push_back(shaderStages);
|
||||
return *this;
|
||||
}
|
||||
|
||||
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
|
||||
const VkVertexInputAttributeDescription& attrib) {
|
||||
mVtxAttribs.push_back(attrib);
|
||||
return *this;
|
||||
}
|
||||
VulkanPipelineStateSerializer& VulkanPipelineStateSerializer::operator<<(
|
||||
const VkVertexInputBindingDescription& binding) {
|
||||
mVtxBindings.push_back(binding);
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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_BACKEND_VULKANPIPELINESTATESERIALIZER_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANPIPELINESTATESERIALIZER_H
|
||||
|
||||
#include "VulkanCommands.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
#include <iostream>
|
||||
#include <type_traits>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
// VulkanPipelineStateSerializer stores to a file the pipeline states.
|
||||
class VulkanPipelineStateSerializer {
|
||||
public:
|
||||
VulkanPipelineStateSerializer(VulkanPipelineStateSerializer const&) = delete;
|
||||
VulkanPipelineStateSerializer& operator=(VulkanPipelineStateSerializer const&) = delete;
|
||||
|
||||
VulkanPipelineStateSerializer(const utils::CString& name);
|
||||
~VulkanPipelineStateSerializer();
|
||||
|
||||
void setID(uint32_t key);
|
||||
void setPipelineLayoutKey(uint32_t pipelineLayoutKey);
|
||||
void setRenderPassKey(uint32_t renderPassKey);
|
||||
VulkanPipelineStateSerializer& operator<<(
|
||||
const VkPipelineInputAssemblyStateCreateInfo& inputAsm);
|
||||
VulkanPipelineStateSerializer& operator<<(const VkPipelineViewportStateCreateInfo& viewport);
|
||||
VulkanPipelineStateSerializer& operator<<(const VkPipelineDynamicStateCreateInfo& dyStates);
|
||||
VulkanPipelineStateSerializer& operator<<(
|
||||
const VkPipelineRasterizationStateCreateInfo& rasterState);
|
||||
VulkanPipelineStateSerializer& operator<<(
|
||||
const VkPipelineDepthStencilStateCreateInfo& depthStencil);
|
||||
VulkanPipelineStateSerializer& operator<<(const VkPipelineMultisampleStateCreateInfo& multiSample);
|
||||
VulkanPipelineStateSerializer& operator<<(
|
||||
const VkPipelineColorBlendStateCreateInfo& blendStateInfo);
|
||||
|
||||
struct ShaderStageInfo {
|
||||
VkShaderStageFlagBits stage;
|
||||
uint64_t module;
|
||||
std::string mName;
|
||||
};
|
||||
VulkanPipelineStateSerializer& operator<<(const ShaderStageInfo& shaderStages);
|
||||
VulkanPipelineStateSerializer& operator<<(
|
||||
const VkVertexInputAttributeDescription& attrib);
|
||||
VulkanPipelineStateSerializer& operator<<(const VkVertexInputBindingDescription& binding);
|
||||
|
||||
private:
|
||||
std::string mFileName;
|
||||
std::stringstream mBuffer;
|
||||
|
||||
//temp data
|
||||
utils::CString mProgramName;
|
||||
std::vector<ShaderStageInfo> mShaderStages;
|
||||
std::vector<VkVertexInputAttributeDescription> mVtxAttribs;
|
||||
std::vector<VkVertexInputBindingDescription> mVtxBindings;
|
||||
};
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif //TNT_FILAMENT_BACKEND_VULKANPIPELINESTATESERIALIZER_H
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 <fstream>
|
||||
|
||||
#include "VulkanRenderPassStateSerializer.h"
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include "VulkanConstants.h"
|
||||
#include "VulkanHandles.h"
|
||||
|
||||
#if defined(__clang__)
|
||||
// Vulkan functions often immediately dereference pointers, so it's fine to pass in a pointer
|
||||
// to a stack-allocated variable.
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wreturn-stack-address"
|
||||
#endif
|
||||
|
||||
using namespace bluevk;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
VulkanRenderPassStateSerializer::VulkanRenderPassStateSerializer(
|
||||
const VkRenderPassCreateInfo& info, uint32_t key) {
|
||||
std::stringstream filename;
|
||||
filename << "render_pass_" << key << ".json";
|
||||
|
||||
std::ofstream file(filename.str());
|
||||
if (file.is_open()) {
|
||||
std::stringstream buffer;
|
||||
buffer << "{" << std::endl;
|
||||
buffer << "\"attachments\":[" << std::endl;
|
||||
for (uint32_t i = 0; i < info.attachmentCount; ++i) {
|
||||
buffer << "{" << std::endl;
|
||||
buffer << "\"flags\":" << info.pAttachments[i].flags << "," << std::endl;
|
||||
buffer << "\"format\":" << info.pAttachments[i].format << "," << std::endl;
|
||||
buffer << "\"samples\":" << info.pAttachments[i].samples << "," << std::endl;
|
||||
buffer << "\"load_op\":" << info.pAttachments[i].loadOp << "," << std::endl;
|
||||
buffer << "\"store_op\":" << info.pAttachments[i].storeOp << "," << std::endl;
|
||||
buffer << "\"stencil_load_op\":" << info.pAttachments[i].stencilLoadOp << "," << std::endl;
|
||||
buffer << "\"stencil_store_op\":" << info.pAttachments[i].stencilStoreOp << "," << std::endl;
|
||||
buffer << "\"initial_layout\":" << info.pAttachments[i].initialLayout << "," << std::endl;
|
||||
buffer << "\"final_layout\":" << info.pAttachments[i].finalLayout << std::endl;
|
||||
buffer << "}";
|
||||
if (i < info.attachmentCount - 1) {
|
||||
buffer << ",";
|
||||
}
|
||||
buffer << std::endl;
|
||||
}
|
||||
buffer << "]," << std::endl;
|
||||
|
||||
buffer << "\"subpasses\":[" << std::endl;
|
||||
for (uint32_t i = 0; i < info.subpassCount; ++i) {
|
||||
buffer << "{" << std::endl;
|
||||
buffer << "\"flags\":" << info.pSubpasses[i].flags << "," << std::endl;
|
||||
buffer << "\"pipeline_bind_point\":" << info.pSubpasses[i].pipelineBindPoint;
|
||||
|
||||
//------------depth stencil-------------------
|
||||
if (info.pSubpasses[i].pDepthStencilAttachment) {
|
||||
buffer << "," << std::endl;
|
||||
buffer << "\"depth_stencil_attachment\":{" << std::endl;
|
||||
buffer << "\"attachement\":"
|
||||
<< info.pSubpasses[i].pDepthStencilAttachment->attachment << ","
|
||||
<< std::endl;
|
||||
buffer << "\"layout\":" << info.pSubpasses[i].pDepthStencilAttachment->layout
|
||||
<< std::endl;
|
||||
buffer << "}";
|
||||
}
|
||||
|
||||
//------------Resolve-------------------
|
||||
if (info.pSubpasses[i].pResolveAttachments) {
|
||||
buffer << "," << std::endl;
|
||||
buffer << "\"resolve_attachments\":[" << std::endl;
|
||||
for (uint32_t j = 0; j < info.pSubpasses[i].colorAttachmentCount; ++j) {
|
||||
buffer << "{" << std::endl;
|
||||
buffer << "\"attachement\":"
|
||||
<< info.pSubpasses[i].pResolveAttachments[j].attachment << ","
|
||||
<< std::endl;
|
||||
buffer << "\"layout\":" << info.pSubpasses[i].pResolveAttachments[j].layout
|
||||
<< std::endl;
|
||||
buffer << "}" << std::endl;
|
||||
if (j < info.pSubpasses[i].colorAttachmentCount - 1) {
|
||||
buffer << ",";
|
||||
}
|
||||
buffer << std::endl;
|
||||
}
|
||||
buffer << "]";
|
||||
}
|
||||
|
||||
//------------Color-------------------
|
||||
if (info.pSubpasses[i].pColorAttachments) {
|
||||
buffer << "," << std::endl;
|
||||
buffer << "\"color_attachments\":[" << std::endl;
|
||||
for (uint32_t j = 0; j < info.pSubpasses[i].colorAttachmentCount; ++j) {
|
||||
buffer << "{" << std::endl;
|
||||
buffer << "\"attachement\":"
|
||||
<< info.pSubpasses[i].pColorAttachments[j].attachment << ","
|
||||
<< std::endl;
|
||||
buffer << "\"layout\":" << info.pSubpasses[i].pColorAttachments[j].layout
|
||||
<< std::endl;
|
||||
buffer << "}" << std::endl;
|
||||
if (j < info.pSubpasses[i].colorAttachmentCount - 1) {
|
||||
buffer << ",";
|
||||
}
|
||||
buffer << std::endl;
|
||||
}
|
||||
buffer << "]";
|
||||
}
|
||||
|
||||
//------------Input-------------------
|
||||
if (info.pSubpasses[i].pInputAttachments) {
|
||||
buffer << "," << std::endl;
|
||||
buffer << "\"input_attachments\":[" << std::endl;
|
||||
for (uint32_t j = 0; j < info.pSubpasses[i].inputAttachmentCount; ++j) {
|
||||
buffer << "{" << std::endl;
|
||||
buffer << "\"attachement\":"
|
||||
<< info.pSubpasses[i].pInputAttachments[j].attachment << ","
|
||||
<< std::endl;
|
||||
buffer << "\"layout\":" << info.pSubpasses[i].pInputAttachments[j].layout
|
||||
<< std::endl;
|
||||
buffer << "}" << std::endl;
|
||||
if (j < info.pSubpasses[i].inputAttachmentCount - 1) {
|
||||
buffer << ",";
|
||||
}
|
||||
buffer << std::endl;
|
||||
}
|
||||
buffer << "]" << std::endl;
|
||||
}
|
||||
|
||||
buffer << "}";
|
||||
if (i < info.subpassCount - 1) {
|
||||
buffer << ",";
|
||||
}
|
||||
buffer << std::endl;
|
||||
}
|
||||
buffer << "]" << std::endl;
|
||||
|
||||
buffer << "}";
|
||||
file << buffer.str();
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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_BACKEND_VULKANRENDERPASSSTATESERIALIZER_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANRENDERPASSSTATESERIALIZER_H
|
||||
|
||||
#include "VulkanCommands.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
#include <iostream>
|
||||
#include <type_traits>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class VulkanRenderPassStateSerializer {
|
||||
public:
|
||||
VulkanRenderPassStateSerializer(VulkanRenderPassStateSerializer const&) = delete;
|
||||
VulkanRenderPassStateSerializer& operator=(VulkanRenderPassStateSerializer const&) = delete;
|
||||
|
||||
VulkanRenderPassStateSerializer(const VkRenderPassCreateInfo& info, uint32_t key);
|
||||
};
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif //TNT_FILAMENT_BACKEND_VULKANRENDERPASSSTATESERIALIZER_H
|
||||
@@ -21,12 +21,14 @@
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include "VulkanSamplerStateSerializer.h"
|
||||
|
||||
using namespace bluevk;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
VulkanSamplerCache::VulkanSamplerCache(VkDevice device)
|
||||
: mDevice(device) {}
|
||||
VulkanSamplerCache::VulkanSamplerCache(VkDevice device, VulkanYcbcrConversionCache* conversionCache)
|
||||
: mDevice(device), mConversionCache(conversionCache) {}
|
||||
|
||||
VkSampler VulkanSamplerCache::getSampler(Params params) {
|
||||
auto iter = mCache.find(params);
|
||||
@@ -57,11 +59,16 @@ VkSampler VulkanSamplerCache::getSampler(Params params) {
|
||||
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
|
||||
.unnormalizedCoordinates = VK_FALSE,
|
||||
};
|
||||
VulkanSamplerStateSerializer samplerSer(params,
|
||||
params.conversion != VK_NULL_HANDLE ? mConversionCache->getKey(params.conversion) : 0);
|
||||
|
||||
VkSampler sampler;
|
||||
VkResult result = vkCreateSampler(mDevice, &samplerInfo, VKALLOC, &sampler);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create sampler."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
mCache.insert({ params, sampler });
|
||||
SamplerHashFn hashFn;
|
||||
mSamplerToKey[sampler] = hashFn(params);
|
||||
return sampler;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKANSAMPLERCACHE_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANSAMPLERCACHE_H
|
||||
|
||||
#include "VulkanYcbcrConversionCache.h"
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/Hash.h>
|
||||
#include <map>
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
#include <tsl/robin_map.h>
|
||||
@@ -37,11 +39,20 @@ public:
|
||||
|
||||
static_assert(sizeof(Params) == 16);
|
||||
|
||||
explicit VulkanSamplerCache(VkDevice device);
|
||||
explicit VulkanSamplerCache(VkDevice device, VulkanYcbcrConversionCache* conversionCache);
|
||||
VkSampler getSampler(Params params);
|
||||
uint32_t getKey(VkSampler sampler) {
|
||||
uint32_t key = 0;
|
||||
auto iter = mSamplerToKey.find(sampler);
|
||||
if (iter != mSamplerToKey.end()) {
|
||||
key = iter->second;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
void terminate() noexcept;
|
||||
private:
|
||||
VkDevice mDevice;
|
||||
VulkanYcbcrConversionCache* mConversionCache;
|
||||
|
||||
struct SamplerEqualTo {
|
||||
bool operator()(Params lhs, Params rhs) const noexcept {
|
||||
@@ -51,6 +62,7 @@ private:
|
||||
};
|
||||
using SamplerHashFn = utils::hash::MurmurHashFn<Params>;
|
||||
tsl::robin_map<Params, VkSampler, SamplerHashFn, SamplerEqualTo> mCache;
|
||||
std::map<VkSampler, uint32_t> mSamplerToKey;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
#include "VulkanSamplerStateSerializer.h"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <utils/Hash.h>
|
||||
|
||||
namespace filament::backend {
|
||||
VulkanYcbcrConversionSerializer::VulkanYcbcrConversionSerializer(
|
||||
const VulkanYcbcrConversionCache::Params& params) {
|
||||
using ConversionHashFn = utils::hash::MurmurHashFn<VulkanYcbcrConversionCache::Params>;
|
||||
ConversionHashFn hashFn;
|
||||
uint32_t key = hashFn(params);
|
||||
std::stringstream filename;
|
||||
filename << "ycbcr_conv_" << key << ".json";
|
||||
|
||||
std::ofstream file(filename.str());
|
||||
if (file.is_open()) {
|
||||
std::stringstream buffer;
|
||||
auto const& chroma = params.conversion;
|
||||
buffer << "{" << std::endl;
|
||||
buffer << "\"format\":" << params.format << "," << std::endl;
|
||||
buffer << "\"external_format\":" << params.externalFormat << "," << std::endl;
|
||||
buffer << "\"ycbcr_model\":" << uint8_t(chroma.ycbcrModel) << "," << std::endl;
|
||||
buffer << "\"ycbcr_range\":" << uint8_t(chroma.ycbcrRange) << "," << std::endl;
|
||||
buffer << "\"swizzle_array\":[" << uint8_t(chroma.r) << "," << uint8_t(chroma.g) << ","
|
||||
<< uint8_t(chroma.b) << "," << uint8_t(chroma.a) << "]," << std::endl;
|
||||
buffer << "\"x_chroma_offset\":" << uint8_t(chroma.xChromaOffset) << "," << std::endl;
|
||||
buffer << "\"y_chroma_offset\":" << uint8_t(chroma.yChromaOffset) << "," << std::endl;
|
||||
buffer << "\"chroma_filter\":" << uint8_t(chroma.chromaFilter) << std::endl;
|
||||
buffer << "}" << std::endl;
|
||||
|
||||
file << buffer.str();
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
VulkanSamplerStateSerializer::VulkanSamplerStateSerializer(const VulkanSamplerCache::Params& params, uint32_t ycbcr_conv_key) {
|
||||
using ConversionHashFn = utils::hash::MurmurHashFn<VulkanSamplerCache::Params>;
|
||||
ConversionHashFn hashFn;
|
||||
uint32_t key = hashFn(params);
|
||||
std::stringstream filename;
|
||||
filename << "sampler_" << key << ".json";
|
||||
|
||||
std::ofstream file(filename.str());
|
||||
if (file.is_open()) {
|
||||
std::stringstream buffer;
|
||||
auto const& samplerParams = params.sampler;
|
||||
buffer << "{" << std::endl;
|
||||
buffer << "\"filter_mag\":" << uint32_t(samplerParams.filterMag) << "," << std::endl;
|
||||
buffer << "\"filter_min\":" << uint32_t(samplerParams.filterMin) << "," << std::endl;
|
||||
buffer << "\"wrap_s\":" << uint32_t(samplerParams.wrapS) << "," << std::endl;
|
||||
buffer << "\"wrap_t\":" << uint32_t(samplerParams.wrapT) << "," << std::endl;
|
||||
buffer << "\"wrap_r\":" << uint32_t(samplerParams.wrapR) << "," << std::endl;
|
||||
buffer << "\"anisotropy_log2\":" << uint32_t(samplerParams.anisotropyLog2) << "," << std::endl;
|
||||
buffer << "\"compare_mode\":" << uint32_t(samplerParams.compareMode) << "," << std::endl;
|
||||
buffer << "\"compare_func\":" << uint32_t(samplerParams.compareFunc) << "," << std::endl;
|
||||
buffer << "\"ycbcr_conv_key\":" << ycbcr_conv_key << std::endl;
|
||||
buffer << "}" << std::endl;
|
||||
|
||||
file << buffer.str();
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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_BACKEND_VULKANSAMPLERSTATESERIALIZER_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANSAMPLERSTATESERIALIZER_H
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include "VulkanYcbcrConversionCache.h"
|
||||
#include "VulkanSamplerCache.h"
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class VulkanYcbcrConversionSerializer {
|
||||
public:
|
||||
VulkanYcbcrConversionSerializer(VulkanYcbcrConversionSerializer const&) = delete;
|
||||
VulkanYcbcrConversionSerializer& operator=(VulkanYcbcrConversionSerializer const&) = delete;
|
||||
|
||||
VulkanYcbcrConversionSerializer(const VulkanYcbcrConversionCache::Params& params);
|
||||
};
|
||||
// VulkanPipelineStateSerializer stores to a file the pipeline states.
|
||||
class VulkanSamplerStateSerializer {
|
||||
public:
|
||||
VulkanSamplerStateSerializer(VulkanSamplerStateSerializer const&) = delete;
|
||||
VulkanSamplerStateSerializer& operator=(VulkanSamplerStateSerializer const&) = delete;
|
||||
|
||||
VulkanSamplerStateSerializer(const VulkanSamplerCache::Params& params, uint32_t ycbcr_conv_key);
|
||||
};
|
||||
}
|
||||
|
||||
#endif //TNT_FILAMENT_BACKEND_VULKANSAMPLERSTATESERIALIZER_H
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include "VulkanYcbcrConversionCache.h"
|
||||
#include "VulkanSamplerStateSerializer.h"
|
||||
|
||||
#include "vulkan/VulkanConstants.h"
|
||||
#include "vulkan/utils/Conversion.h"
|
||||
@@ -53,6 +54,7 @@ VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
|
||||
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
|
||||
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
|
||||
};
|
||||
VulkanYcbcrConversionSerializer ycbcr_ser(params);
|
||||
|
||||
// We could put this in the platform class, but that seems like a bit of an overkill
|
||||
#if defined(__ANDROID__)
|
||||
@@ -73,6 +75,8 @@ VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
|
||||
mCache.insert({ params, conversion });
|
||||
ConversionHashFn hashFn;
|
||||
mConversionToKey[conversion] = hashFn(params);
|
||||
return conversion;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
#include <tsl/robin_map.h>
|
||||
#include <map>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -40,6 +41,14 @@ public:
|
||||
|
||||
explicit VulkanYcbcrConversionCache(VkDevice device);
|
||||
VkSamplerYcbcrConversion getConversion(Params params);
|
||||
uint32_t getKey(VkSamplerYcbcrConversion conv) {
|
||||
uint32_t key = 0;
|
||||
auto iter = mConversionToKey.find(conv);
|
||||
if (iter != mConversionToKey.end()) {
|
||||
key = iter->second;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
void terminate() noexcept;
|
||||
|
||||
private:
|
||||
@@ -54,6 +63,7 @@ private:
|
||||
};
|
||||
using ConversionHashFn = utils::hash::MurmurHashFn<Params>;
|
||||
tsl::robin_map<Params, VkSamplerYcbcrConversion, ConversionHashFn, ConversionEqualTo> mCache;
|
||||
std::map<VkSamplerYcbcrConversion, uint32_t> mConversionToKey;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -228,10 +228,6 @@ ExtensionSet getDeviceExtensions(VkPhysicalDevice device) {
|
||||
VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME,
|
||||
#endif
|
||||
VK_KHR_MULTIVIEW_EXTENSION_NAME,
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
|
||||
VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME,
|
||||
#endif
|
||||
};
|
||||
ExtensionSet exts;
|
||||
// Identify supported physical device extensions
|
||||
@@ -869,8 +865,6 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
||||
if (!mImpl->mSharedContext) {
|
||||
context.mDebugUtilsSupported = setContains(instExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||
context.mDebugMarkersSupported = setContains(deviceExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
|
||||
context.mPipelineCreationFeedbackSupported =
|
||||
setContains(deviceExts, VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME);
|
||||
} else {
|
||||
VulkanSharedContext const* scontext = (VulkanSharedContext const*) sharedContext;
|
||||
context.mDebugUtilsSupported = scontext->debugUtilsSupported;
|
||||
@@ -879,16 +873,13 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
||||
|
||||
// Check the availability of lazily allocated memory
|
||||
context.mLazilyAllocatedMemorySupported = false;
|
||||
// RenderDoc doesn't support lazy allocated memory
|
||||
if constexpr (!FVK_RENDERDOC_CAPTURE_MODE) {
|
||||
for (uint32_t i = 0, typeCount = context.mMemoryProperties.memoryTypeCount; i < typeCount;
|
||||
++i) {
|
||||
VkMemoryType const type = context.mMemoryProperties.memoryTypes[i];
|
||||
if (type.propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) {
|
||||
context.mLazilyAllocatedMemorySupported = true;
|
||||
assert_invariant(type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
break;
|
||||
}
|
||||
for (uint32_t i = 0, typeCount = context.mMemoryProperties.memoryTypeCount; i < typeCount;
|
||||
++i) {
|
||||
VkMemoryType const type = context.mMemoryProperties.memoryTypes[i];
|
||||
if (type.propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) {
|
||||
context.mLazilyAllocatedMemorySupported = true;
|
||||
assert_invariant(type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1031,8 +1022,4 @@ VkExternalFenceHandleTypeFlagBits VulkanPlatform::getFenceExportFlags() const no
|
||||
return static_cast<VkExternalFenceHandleTypeFlagBits>(0);
|
||||
}
|
||||
|
||||
bool VulkanPlatform::isTransientAttachmentSupported() const noexcept {
|
||||
return mImpl->mContext.isLazilyAllocatedMemorySupported();
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <backend/platforms/VulkanPlatformAndroid.h>
|
||||
|
||||
#include "vulkan/VulkanConstants.h"
|
||||
@@ -21,8 +20,6 @@
|
||||
#include "vulkan/platform/VulkanPlatformSwapChainImpl.h"
|
||||
#include "vulkan/utils/Image.h"
|
||||
|
||||
#include "AndroidFrameCallback.h"
|
||||
|
||||
#include <private/backend/BackendUtilsAndroid.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
@@ -35,7 +32,6 @@
|
||||
#include <android/hardware_buffer.h>
|
||||
#include <android/native_window.h>
|
||||
|
||||
#include <new>
|
||||
#include <utility>
|
||||
|
||||
using namespace bluevk;
|
||||
@@ -162,55 +158,7 @@ std::pair<TextureFormat, TextureUsage> getFilamentFormatAndUsage(const AHardware
|
||||
};
|
||||
}
|
||||
|
||||
uint32_t selectMemoryTypeForExternalImage(VkPhysicalDevice physicalDevice, VkDevice device,
|
||||
VkImage image, uint32_t types, VkFlags requiredMemoryFlags) {
|
||||
VkPhysicalDeviceMemoryProperties memoryProperties;
|
||||
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties);
|
||||
uint32_t const memoryTypeIndex =
|
||||
VulkanContext::selectMemoryType(memoryProperties, types, requiredMemoryFlags);
|
||||
|
||||
if constexpr (FVK_RENDERDOC_CAPTURE_MODE) {
|
||||
// RenderDoc will replay external resources as non-external.
|
||||
// Adjust properties that will trip it up when replaying, even though these are not valid.
|
||||
// Update memory type index if necessary so that we can replay the capture.
|
||||
|
||||
VkMemoryRequirements imageMemoryRequirements;
|
||||
vkGetImageMemoryRequirements(device, image, &imageMemoryRequirements);
|
||||
|
||||
uint32_t const imageMemoryTypeBits = imageMemoryRequirements.memoryTypeBits;
|
||||
bool const isMemoryTypeSupported = ((1 << memoryTypeIndex) & imageMemoryTypeBits) != 0;
|
||||
if (isMemoryTypeSupported) {
|
||||
return memoryTypeIndex;
|
||||
}
|
||||
|
||||
// Current memory type will not be replayable by RenderDoc.
|
||||
// Attempt to change the memory type index
|
||||
VkMemoryPropertyFlags const kRenderDocFallBackReqs = 0;
|
||||
uint32_t commonMemoryTypeBits = types & imageMemoryTypeBits;
|
||||
uint32_t commonTypeIndex = VulkanContext::selectMemoryType(memoryProperties,
|
||||
commonMemoryTypeBits, kRenderDocFallBackReqs);
|
||||
if (commonMemoryTypeBits && commonTypeIndex != VK_MAX_MEMORY_TYPES) {
|
||||
return commonTypeIndex;
|
||||
}
|
||||
uint32_t imageTypeIndex = VulkanContext::selectMemoryType(memoryProperties,
|
||||
imageMemoryTypeBits, kRenderDocFallBackReqs);
|
||||
if (imageTypeIndex != VK_MAX_MEMORY_TYPES) {
|
||||
return imageTypeIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return memoryTypeIndex;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
struct VulkanPlatformAndroid::AndroidDetails {
|
||||
AndroidFrameCallback androidFrameCallback;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
}// namespace
|
||||
|
||||
VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() {
|
||||
if (__builtin_available(android 26, *)) {
|
||||
@@ -220,19 +168,14 @@ VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid()
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
VulkanPlatformAndroid::VulkanPlatformAndroid() :
|
||||
mAndroidDetails(*(new(std::nothrow) AndroidDetails{})) {
|
||||
VulkanPlatformAndroid::VulkanPlatformAndroid() {
|
||||
mOSVersion = android_get_device_api_level();
|
||||
if (mOSVersion < 0) {
|
||||
mOSVersion = __ANDROID_API_FUTURE__;
|
||||
}
|
||||
}
|
||||
|
||||
VulkanPlatformAndroid::~VulkanPlatformAndroid() noexcept {
|
||||
delete &mAndroidDetails;
|
||||
}
|
||||
VulkanPlatformAndroid::~VulkanPlatformAndroid() noexcept = default;
|
||||
|
||||
Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage(
|
||||
AHardwareBuffer const* buffer, bool sRGB) noexcept {
|
||||
@@ -411,6 +354,8 @@ VulkanPlatform::ImageData VulkanPlatformAndroid::createVkImageFromExternal(
|
||||
.image = image,
|
||||
.buffer = VK_NULL_HANDLE,
|
||||
};
|
||||
VkPhysicalDeviceMemoryProperties memoryProperties;
|
||||
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties);
|
||||
VkMemoryPropertyFlags requiredMemoryFlags =
|
||||
!isExternal && any(metadata.filamentUsage & TextureUsage::UPLOADABLE)
|
||||
? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
||||
@@ -420,8 +365,8 @@ VulkanPlatform::ImageData VulkanPlatformAndroid::createVkImageFromExternal(
|
||||
requiredMemoryFlags |= VK_MEMORY_PROPERTY_PROTECTED_BIT;
|
||||
}
|
||||
|
||||
uint32_t const memoryTypeIndex = selectMemoryTypeForExternalImage(physicalDevice, device,
|
||||
image, metadata.memoryTypeBits, requiredMemoryFlags);
|
||||
uint32_t const memoryTypeIndex = VulkanContext::selectMemoryType(memoryProperties,
|
||||
metadata.memoryTypeBits, requiredMemoryFlags);
|
||||
|
||||
VkMemoryAllocateInfo const allocInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
@@ -514,14 +459,14 @@ int VulkanPlatformAndroid::getOSVersion() const noexcept {
|
||||
}
|
||||
|
||||
void VulkanPlatformAndroid::terminate() {
|
||||
mAndroidDetails.androidFrameCallback.terminate();
|
||||
mAndroidFrameCallback.terminate();
|
||||
VulkanPlatform::terminate();
|
||||
}
|
||||
|
||||
Driver* VulkanPlatformAndroid::createDriver(void* sharedContext, DriverConfig const& driverConfig) {
|
||||
Driver* driver = VulkanPlatform::createDriver(sharedContext, driverConfig);
|
||||
if (driver) {
|
||||
mAndroidDetails.androidFrameCallback.init();
|
||||
mAndroidFrameCallback.init();
|
||||
}
|
||||
return driver;
|
||||
}
|
||||
@@ -537,19 +482,13 @@ bool VulkanPlatformAndroid::queryCompositorTiming(SwapChain const* swapchain,
|
||||
}
|
||||
|
||||
AndroidFrameCallback::Timeline const preferredTimeline{
|
||||
mAndroidDetails.androidFrameCallback.getPreferredTimeline() };
|
||||
mAndroidFrameCallback.getPreferredTimeline() };
|
||||
outCompositorTiming->frameTime = preferredTimeline.frameTime;
|
||||
outCompositorTiming->expectedPresentTime = preferredTimeline.expectedPresentTime;
|
||||
outCompositorTiming->frameTimelineDeadline = preferredTimeline.frameTimelineDeadline;
|
||||
outCompositorTiming->compositeDeadline = CompositorTiming::INVALID;
|
||||
outCompositorTiming->compositeInterval = CompositorTiming::INVALID;
|
||||
outCompositorTiming->compositeToPresentLatency = CompositorTiming::INVALID;
|
||||
|
||||
// From this point on, we always return "success" because some timings were returned.
|
||||
|
||||
auto vulkanSwapchain = static_cast<VulkanPlatformSwapChainBase const *>(swapchain);
|
||||
vulkanSwapchain->queryCompositorTiming(outCompositorTiming);
|
||||
return true;
|
||||
return vulkanSwapchain->queryCompositorTiming(outCompositorTiming);
|
||||
}
|
||||
|
||||
bool VulkanPlatformAndroid::setPresentFrameId(SwapChain const* swapchain, uint64_t frameId) noexcept {
|
||||
|
||||
@@ -165,11 +165,6 @@ VulkanPlatformSurfaceSwapChain::~VulkanPlatformSurfaceSwapChain() {
|
||||
VkResult VulkanPlatformSurfaceSwapChain::create() {
|
||||
#ifdef __ANDROID__
|
||||
NativeWindow::enableFrameTimestamps(static_cast<ANativeWindow*>(mNativeWindow), true);
|
||||
// on Android, disable producer throttling
|
||||
if (mProducerThrottling.isSupported()) {
|
||||
mProducerThrottling.setProducerThrottlingEnabled(
|
||||
static_cast<ANativeWindow*>(mNativeWindow), false);
|
||||
}
|
||||
#endif
|
||||
|
||||
VkSurfaceFormatKHR surfaceFormat = {};
|
||||
@@ -361,15 +356,13 @@ bool VulkanPlatformSurfaceSwapChain::queryCompositorTiming(
|
||||
CompositorTiming* outCompositorTiming) const {
|
||||
#ifdef __ANDROID__
|
||||
// fallback to private APIs
|
||||
if (UTILS_VERY_LIKELY(mNativeWindow)) {
|
||||
int const status = NativeWindow::getCompositorTiming(
|
||||
static_cast<ANativeWindow*>(mNativeWindow),
|
||||
&outCompositorTiming->compositeDeadline,
|
||||
&outCompositorTiming->compositeInterval,
|
||||
&outCompositorTiming->compositeToPresentLatency);
|
||||
if (status == 0) {
|
||||
return true;
|
||||
}
|
||||
int const status = NativeWindow::getCompositorTiming(
|
||||
static_cast<ANativeWindow*>(mNativeWindow),
|
||||
&outCompositorTiming->compositeDeadline,
|
||||
&outCompositorTiming->compositeInterval,
|
||||
&outCompositorTiming->compositeToPresentLatency);
|
||||
if (status == 0) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return VulkanPlatformSwapChainBase::queryCompositorTiming(outCompositorTiming);
|
||||
|
||||
@@ -25,8 +25,7 @@
|
||||
#include <bluevk/BlueVK.h>
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "AndroidSwapChainHelper.h"
|
||||
#include "AndroidNativeWindow.h"
|
||||
#include <AndroidSwapChainHelper.h>
|
||||
#endif
|
||||
|
||||
#include <unordered_map>
|
||||
@@ -123,7 +122,6 @@ private:
|
||||
|
||||
#ifdef __ANDROID__
|
||||
AndroidSwapChainHelper mImpl{};
|
||||
AndroidProducerThrottling mProducerThrottling;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -113,10 +113,8 @@ public:
|
||||
/**
|
||||
* Retrieve a history of frame timing information. The maximum frame history size is
|
||||
* given by getMaxFrameHistorySize().
|
||||
* All or part of the history can be lost when using a different SwapChain in beginFrame().
|
||||
* @param historySize requested history size. The returned vector could be smaller.
|
||||
* @return A vector of FrameInfo.
|
||||
* @see beginFrame()
|
||||
*/
|
||||
utils::FixedCapacityVector<FrameInfo> getFrameInfoHistory(
|
||||
size_t historySize = 1) const noexcept;
|
||||
@@ -328,8 +326,6 @@ public:
|
||||
* or 0 if unknown. This value should be the timestamp of
|
||||
* the last h/w vsync. It is expressed in the
|
||||
* std::chrono::steady_clock time base.
|
||||
* On Android this should be the frame time received from
|
||||
* a Choreographer.
|
||||
* @param swapChain A pointer to the SwapChain instance to use.
|
||||
*
|
||||
* @return
|
||||
@@ -341,8 +337,6 @@ public:
|
||||
*
|
||||
* @note
|
||||
* All calls to render() must happen *after* beginFrame().
|
||||
* It is recommended to use the same swapChain for every call to beginFrame, failing to do
|
||||
* so can result is losing all or part of the FrameInfo history.
|
||||
*
|
||||
* @see
|
||||
* endFrame()
|
||||
|
||||
@@ -258,10 +258,6 @@ void PostProcessManager::bindPerRenderableDescriptorSet(DriverApi& driver) const
|
||||
{ { 0, 0 }, driver });
|
||||
}
|
||||
|
||||
UboManager* PostProcessManager::getUboManager() const noexcept {
|
||||
return mEngine.getUboManager();
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
void PostProcessManager::registerPostProcessMaterial(std::string_view const name,
|
||||
StaticMaterialInfo const& info) {
|
||||
@@ -517,7 +513,7 @@ UTILS_NOINLINE
|
||||
void PostProcessManager::commitAndRenderFullScreenQuad(DriverApi& driver,
|
||||
FrameGraphResources::RenderPassInfo const& out, FMaterialInstance const* mi,
|
||||
PostProcessVariant const variant) const noexcept {
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
FMaterial const* const ma = mi->getMaterial();
|
||||
PipelineState const pipeline = getPipelineState(ma, variant);
|
||||
@@ -649,7 +645,7 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
|
||||
auto th = driver.createTextureView(in, level, 1);
|
||||
mi->setParameter("depth", th, SamplerParams{
|
||||
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
renderFullScreenQuad(out, pipeline, driver);
|
||||
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL);
|
||||
@@ -1083,7 +1079,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
|
||||
mi->setParameter("ssctRayCount",
|
||||
float2{ options.ssct.rayCount, 1.0f / float(options.ssct.rayCount) });
|
||||
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
|
||||
auto pipeline = getPipelineState(ma);
|
||||
@@ -1204,7 +1200,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::bilateralBlurPass(FrameGraph
|
||||
mi->setParameter("sampleCount", kGaussianCount);
|
||||
mi->setParameter("farPlaneOverEdgeDistance", -zf / config.bilateralThreshold);
|
||||
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
|
||||
auto pipeline = getPipelineState(ma);
|
||||
@@ -1899,7 +1895,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
|
||||
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
|
||||
mi->setParameter("weightScale", 0.5f / float(1u << level)); // FIXME: halfres?
|
||||
mi->setParameter("texelSize", float2{ 1.0f / w, 1.0f / h });
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
|
||||
renderFullScreenQuad(out, pipeline, driver);
|
||||
@@ -2429,7 +2425,7 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
|
||||
mi->setParameter("source", hwOutView, SamplerParams{
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST});
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
renderFullScreenQuad(hwDstRT, pipeline, driver);
|
||||
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL);
|
||||
@@ -2537,7 +2533,7 @@ void PostProcessManager::colorGradingPrepareSubpass(DriverApi& driver,
|
||||
FMaterialInstance* const mi =
|
||||
configureColorGradingMaterial(material, colorGrading, colorGradingConfig,
|
||||
vignetteOptions, width, height);
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->commit(driver);
|
||||
}
|
||||
|
||||
void PostProcessManager::colorGradingSubpass(DriverApi& driver,
|
||||
@@ -2570,7 +2566,7 @@ void PostProcessManager::customResolvePrepareSubpass(DriverApi& driver, CustomRe
|
||||
auto [mi, fixedIndex] = mMaterialInstanceManager.getFixedMaterialInstance(ma);
|
||||
mFixedMaterialInstanceIndex.customResolve = fixedIndex;
|
||||
mi->setParameter("direction", op == CustomResolveOp::COMPRESS ? 1.0f : -1.0f),
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->commit(driver);
|
||||
material.getMaterial(mEngine);
|
||||
}
|
||||
|
||||
@@ -2623,7 +2619,7 @@ void PostProcessManager::clearAncillaryBuffersPrepare(DriverApi& driver) noexcep
|
||||
auto ma = material.getMaterial(mEngine, PostProcessVariant::OPAQUE);
|
||||
auto [mi, fixedIndex] = mMaterialInstanceManager.getFixedMaterialInstance(ma);
|
||||
mFixedMaterialInstanceIndex.clearDepth = fixedIndex;
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->commit(driver);
|
||||
material.getMaterial(mEngine);
|
||||
}
|
||||
|
||||
@@ -3115,7 +3111,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
|
||||
mat4f{ historyProjection * inverse(current.projection) } *
|
||||
normalizedToClip);
|
||||
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
|
||||
if (colorGradingConfig.asSubpass) {
|
||||
@@ -3200,7 +3196,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::rcas(
|
||||
mi->setParameter("resolution", float4{
|
||||
outputDesc.width, outputDesc.height,
|
||||
1.0f / outputDesc.width, 1.0f / outputDesc.height });
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
|
||||
auto pipeline = getPipelineState(material.getMaterial(mEngine), variant);
|
||||
@@ -3291,7 +3287,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleBilinear(FrameGraph&
|
||||
float(vp.width) / inputDesc.width,
|
||||
float(vp.height) / inputDesc.height
|
||||
});
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
|
||||
auto out = resources.getRenderPassInfo();
|
||||
@@ -3378,7 +3374,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleSGSR1(FrameGraph& fg,
|
||||
float(inputDesc.height)
|
||||
});
|
||||
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
|
||||
auto const out = resources.getRenderPassInfo();
|
||||
@@ -3473,7 +3469,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleFSR1(FrameGraph& fg,
|
||||
mi->setParameter("resolution",
|
||||
float4{ outputDesc.width, outputDesc.height,
|
||||
1.0f / outputDesc.width, 1.0f / outputDesc.height });
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
}
|
||||
|
||||
@@ -3500,7 +3496,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleFSR1(FrameGraph& fg,
|
||||
float(vp.width) / inputDesc.width,
|
||||
float(vp.height) / inputDesc.height
|
||||
});
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
}
|
||||
|
||||
@@ -3586,7 +3582,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool co
|
||||
if (layer) {
|
||||
mi->setParameter("layerIndex", layer);
|
||||
}
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
|
||||
auto pipeline = getPipelineState(ma);
|
||||
@@ -3844,7 +3840,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::vsmMipmapPass(FrameGraph& fg
|
||||
});
|
||||
mi->setParameter("layer", uint32_t(layer));
|
||||
mi->setParameter("uvscale", 1.0f / float(dim));
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
|
||||
renderFullScreenQuadWithScissor(out, pipeline, scissor, driver);
|
||||
@@ -3946,7 +3942,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugCombineArrayTexture(Fra
|
||||
float(vp.width) / inputDesc.width,
|
||||
float(vp.height) / inputDesc.height
|
||||
});
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
|
||||
auto pipeline = getPipelineState(ma);
|
||||
@@ -3963,7 +3959,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugCombineArrayTexture(Fra
|
||||
// Render all layers of the texture to the screen side-by-side.
|
||||
for (uint32_t i = 0; i < inputTextureDesc.depth; ++i) {
|
||||
mi->setParameter("layerIndex", i);
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->commit(driver);
|
||||
renderFullScreenQuad(out, pipeline, driver);
|
||||
DescriptorSet::unbind(driver, DescriptorSetBindingPoints::PER_MATERIAL);
|
||||
// From the second draw, don't clear the targetbuffer.
|
||||
|
||||
@@ -65,7 +65,6 @@ class FMaterialInstance;
|
||||
class FrameGraph;
|
||||
class RenderPass;
|
||||
class RenderPassBuilder;
|
||||
class UboManager;
|
||||
struct CameraInfo;
|
||||
|
||||
class PostProcessManager {
|
||||
@@ -423,8 +422,6 @@ private:
|
||||
return getMaterialInstance(ma);
|
||||
}
|
||||
|
||||
UboManager* getUboManager() const noexcept;
|
||||
|
||||
backend::RenderPrimitiveHandle mFullScreenQuadRph;
|
||||
backend::VertexBufferInfoHandle mFullScreenQuadVbih;
|
||||
backend::DescriptorSetLayoutHandle mPerRenderableDslh;
|
||||
|
||||
+15
-25
@@ -1012,8 +1012,6 @@ void RenderPass::Executor::execute(FEngine const& engine, DriverApi& driver,
|
||||
uint32_t const index = (first->key & CUSTOM_INDEX_MASK) >> CUSTOM_INDEX_SHIFT;
|
||||
assert_invariant(index < mCustomCommands.size());
|
||||
pCustomCommands[index]();
|
||||
currentPipeline = {};
|
||||
currentPrimitiveHandle ={};
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1071,34 +1069,26 @@ void RenderPass::Executor::execute(FEngine const& engine, DriverApi& driver,
|
||||
pipeline.pipelineLayout.setLayout[+DescriptorSetBindingPoints::PER_MATERIAL] =
|
||||
ma->getDescriptorSetLayout(info.materialVariant).getHandle();
|
||||
|
||||
// If we have a ColorPassDescriptorSet we use it to bind the per-view
|
||||
// descriptor-set (ideally only if it changed).
|
||||
// If we don't, it means the descriptor-set is already bound and the layout we
|
||||
// got from the material above should match. This is the case for situations
|
||||
// where we have a known per-view descriptor-set layout,
|
||||
// e.g.: postfx, shadow-maps, ssr and structure passes.
|
||||
if (mColorPassDescriptorSet) {
|
||||
if (UTILS_UNLIKELY(ma->getMaterialDomain() == MaterialDomain::POST_PROCESS)) {
|
||||
// It is possible to get a post-process material here (even though it's
|
||||
// not technically a public API yet, it is used by the IBLPrefilterLibrary.
|
||||
// Ideally we would have a more formal compute API). In this case, we need
|
||||
// to set the post-process descriptor-set.
|
||||
engine.getPostProcessManager().bindPostProcessDescriptorSet(driver);
|
||||
} else {
|
||||
|
||||
if (UTILS_UNLIKELY(ma->getMaterialDomain() == MaterialDomain::POST_PROCESS)) {
|
||||
// It is possible to get a post-process material here (even though it's
|
||||
// not technically a public API yet, it is used by the IBLPrefilterLibrary.
|
||||
// Ideally we would have a more formal compute API). In this case, we need
|
||||
// to set the post-process descriptor-set.
|
||||
engine.getPostProcessManager().bindPostProcessDescriptorSet(driver);
|
||||
} else {
|
||||
// If we have a ColorPassDescriptorSet we use it to bind the per-view
|
||||
// descriptor-set (ideally only if it changed). If we don't, it means
|
||||
// the descriptor-set is already bound and the layout we got from the
|
||||
// material above should match. This is the case for situations where we
|
||||
// have a known per-view descriptor-set layout, e.g.: shadow-maps, ssr and
|
||||
// structure passes.
|
||||
if (mColorPassDescriptorSet) {
|
||||
// We have a ColorPassDescriptorSet, we need to go through it for binding
|
||||
// the per-view descriptor-set because its layout can change based on the
|
||||
// material.
|
||||
mColorPassDescriptorSet->bind(driver, ma->getPerViewLayoutIndex());
|
||||
}
|
||||
} else {
|
||||
// if we're here it means the per-view descriptor set is constant and
|
||||
// already set. This will be the case for postfx, ssr, structure and
|
||||
// shadow passes. All these passes use a static descriptor set layout
|
||||
// (albeit potentially different for each pass). In particular the
|
||||
// per-view UBO must be compatible for all material domains.
|
||||
// This is the case by construction for postfx, ssr. However, shadows
|
||||
// and structure have their own UBO, but it's content is (must be)
|
||||
// compatible with POST_PROCESS and COMPUTE materials.
|
||||
}
|
||||
|
||||
// Each MaterialInstance has its own descriptor set. This binds it.
|
||||
|
||||
@@ -142,11 +142,7 @@ void BufferAllocator::retire(AllocationId id) {
|
||||
InternalSlotNode* targetNode = getNodeById(id);
|
||||
assert_invariant(targetNode != nullptr);
|
||||
|
||||
Slot& slot = targetNode->slot;
|
||||
slot.isAllocated = false;
|
||||
if (slot.gpuUseCount == 0) {
|
||||
mHasPendingFrees = true;
|
||||
}
|
||||
targetNode->slot.isAllocated = false;
|
||||
}
|
||||
|
||||
void BufferAllocator::acquireGpu(AllocationId id) {
|
||||
@@ -161,18 +157,10 @@ void BufferAllocator::releaseGpu(AllocationId id) {
|
||||
assert_invariant(targetNode != nullptr);
|
||||
assert_invariant(targetNode->slot.gpuUseCount > 0);
|
||||
|
||||
Slot& slot = targetNode->slot;
|
||||
slot.gpuUseCount--;
|
||||
if (slot.gpuUseCount == 0 && !slot.isAllocated) {
|
||||
mHasPendingFrees = true;
|
||||
}
|
||||
targetNode->slot.gpuUseCount--;
|
||||
}
|
||||
|
||||
void BufferAllocator::releaseFreeSlots() {
|
||||
if (!mHasPendingFrees) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto curr = mSlotPool.begin();
|
||||
while (curr != mSlotPool.end()) {
|
||||
if (!curr->slot.isFree()) {
|
||||
@@ -211,7 +199,6 @@ void BufferAllocator::releaseFreeSlots() {
|
||||
|
||||
curr = next;
|
||||
}
|
||||
mHasPendingFrees = false;
|
||||
}
|
||||
|
||||
BufferAllocator::allocation_size_t BufferAllocator::getTotalSize() const noexcept {
|
||||
|
||||
@@ -113,9 +113,8 @@ private:
|
||||
|
||||
[[nodiscard]] InternalSlotNode* getNodeById(AllocationId id) const noexcept;
|
||||
|
||||
bool mHasPendingFrees = false;
|
||||
allocation_size_t mTotalSize;
|
||||
const allocation_size_t mSlotSize; // Size of a single slot in bytes
|
||||
const allocation_size_t mSlotSize; // Size of a single slot in bytes.
|
||||
std::list<InternalSlotNode> mSlotPool; // All slots, including both allocated and freed
|
||||
std::multimap</*slot size*/allocation_size_t, InternalSlotNode*> mFreeList;
|
||||
std::unordered_map</*slot offset*/allocation_size_t, InternalSlotNode*> mOffsetMap;
|
||||
|
||||
@@ -468,26 +468,11 @@ void FEngine::init() {
|
||||
}
|
||||
mDefaultMaterial = downcast(defaultMaterialBuilder.build(*this));
|
||||
}
|
||||
|
||||
// We must commit the default material instance here. It may not be used in a scene, but its
|
||||
// descriptor set may still be used for shared variants.
|
||||
//
|
||||
// Note that this material instance is instantiated before the creation of UboManager, so at
|
||||
// this point `isUboBatchingEnabled` is `false`, and it will fall back to individual UBO
|
||||
// automatically.
|
||||
mDefaultMaterial->getDefaultInstance()->commit(driverApi, mUboManager);
|
||||
mDefaultMaterial->getDefaultInstance()->commit(driverApi);
|
||||
|
||||
if (UTILS_UNLIKELY(getSupportedFeatureLevel() >= FeatureLevel::FEATURE_LEVEL_1)) {
|
||||
// UBO batching is not supported in feature level 0
|
||||
if (features.material.enable_material_instance_uniform_batching) {
|
||||
// Ubo size of each material instance is at least 16 bytes.
|
||||
constexpr BufferAllocator::allocation_size_t minSlotSize = 16;
|
||||
auto const uboOffsetAlignment = static_cast<BufferAllocator::allocation_size_t>(
|
||||
driverApi.getUniformBufferOffsetAlignment());
|
||||
BufferAllocator::allocation_size_t slotSize = std::max(minSlotSize, uboOffsetAlignment);
|
||||
mUboManager = new UboManager(getDriverApi(), slotSize, mConfig.sharedUboInitialSizeInBytes);
|
||||
}
|
||||
|
||||
mDefaultColorGrading = downcast(ColorGrading::Builder().build(*this));
|
||||
|
||||
constexpr float3 dummyPositions[1] = {};
|
||||
@@ -668,12 +653,6 @@ void FEngine::shutdown() {
|
||||
|
||||
driver.destroyRenderTarget(std::move(mDefaultRenderTarget));
|
||||
|
||||
if (isUboBatchingEnabled()) {
|
||||
mUboManager->terminate(driver);
|
||||
delete mUboManager;
|
||||
mUboManager = nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Shutdown the backend...
|
||||
*/
|
||||
@@ -714,31 +693,18 @@ void FEngine::prepare() {
|
||||
// UBOs that are visible only. It's not such a big issue because the actual upload() is
|
||||
// skipped if the UBO hasn't changed. Still we could have a lot of these.
|
||||
DriverApi& driver = getDriverApi();
|
||||
const bool useUboBatching = isUboBatchingEnabled();
|
||||
|
||||
if (useUboBatching) {
|
||||
assert_invariant(mUboManager != nullptr);
|
||||
|
||||
mUboManager->beginFrame(driver, mMaterialInstances);
|
||||
}
|
||||
|
||||
UboManager* uboManager = mUboManager;
|
||||
for (auto& materialInstanceList: mMaterialInstances) {
|
||||
materialInstanceList.second.forEach([&driver, uboManager](FMaterialInstance* item) {
|
||||
materialInstanceList.second.forEach([&driver](FMaterialInstance* item) {
|
||||
// post-process materials instances must be commited explicitly because their
|
||||
// parameters are typically not set at this point in time.
|
||||
if (item->getMaterial()->getMaterialDomain() == MaterialDomain::SURFACE) {
|
||||
item->commitStreamUniformAssociations(driver);
|
||||
item->commit(driver, uboManager);
|
||||
item->commit(driver);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (useUboBatching) {
|
||||
assert_invariant(mUboManager != nullptr);
|
||||
getUboManager()->finishBeginFrame(getDriverApi());
|
||||
}
|
||||
|
||||
mMaterials.forEach([](FMaterial* material) {
|
||||
#if FILAMENT_ENABLE_MATDBG // NOLINT(*-include-cleaner)
|
||||
material->checkProgramEdits();
|
||||
@@ -755,13 +721,6 @@ void FEngine::gc() {
|
||||
mCameraManager.gc(*this, em);
|
||||
}
|
||||
|
||||
void FEngine::submitFrame() {
|
||||
if (isUboBatchingEnabled()) {
|
||||
DriverApi& driver = getDriverApi();
|
||||
getUboManager()->endFrame(driver, getMaterialInstanceResourceList());
|
||||
}
|
||||
}
|
||||
|
||||
void FEngine::flush() {
|
||||
// flush the command buffer
|
||||
flushCommandBuffer(mCommandBufferQueue);
|
||||
@@ -992,10 +951,9 @@ FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material,
|
||||
return p;
|
||||
}
|
||||
|
||||
FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material, const char* name,
|
||||
UboBatchingMode batchingMode) noexcept {
|
||||
FMaterialInstance* p =
|
||||
mHeapAllocator.make<FMaterialInstance>(*this, material, name, batchingMode);
|
||||
FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material,
|
||||
const char* name) noexcept {
|
||||
FMaterialInstance* p = mHeapAllocator.make<FMaterialInstance>(*this, material, name);
|
||||
if (UTILS_LIKELY(p)) {
|
||||
auto pos = mMaterialInstances.emplace(material, "MaterialInstance");
|
||||
pos.first->second.insert(p);
|
||||
@@ -1285,11 +1243,6 @@ UTILS_NOINLINE
|
||||
bool FEngine::destroy(const FMaterialInstance* p) {
|
||||
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
|
||||
// To do this, we currently need to inspect all render primitives in the RenderableManager
|
||||
EntityManager const& em = mEntityManager;
|
||||
|
||||
@@ -21,17 +21,16 @@
|
||||
|
||||
#include "Allocators.h"
|
||||
#include "DFG.h"
|
||||
#include "PostProcessManager.h"
|
||||
#include "ResourceList.h"
|
||||
#include "HwDescriptorSetLayoutFactory.h"
|
||||
#include "HwVertexBufferInfoFactory.h"
|
||||
#include "MaterialCache.h"
|
||||
#include "PostProcessManager.h"
|
||||
#include "ResourceList.h"
|
||||
#include "UboManager.h"
|
||||
|
||||
#include "components/CameraManager.h"
|
||||
#include "components/LightManager.h"
|
||||
#include "components/RenderableManager.h"
|
||||
#include "components/TransformManager.h"
|
||||
#include "components/RenderableManager.h"
|
||||
|
||||
#include "ds/DescriptorSetLayout.h"
|
||||
|
||||
@@ -264,14 +263,6 @@ public:
|
||||
return mBackend;
|
||||
}
|
||||
|
||||
bool isUboBatchingEnabled() const noexcept {
|
||||
return mUboManager != nullptr;
|
||||
}
|
||||
|
||||
UboManager* getUboManager() noexcept {
|
||||
return mUboManager;
|
||||
}
|
||||
|
||||
Platform* getPlatform() const noexcept {
|
||||
return mPlatform;
|
||||
}
|
||||
@@ -345,21 +336,11 @@ public:
|
||||
|
||||
FRenderer* createRenderer() noexcept;
|
||||
|
||||
// Defines whether a material instance should use UBO batching or not.
|
||||
enum class UboBatchingMode {
|
||||
// For default, it follows the engine settings.
|
||||
// If UBO batching is enabled on the engine and the material domain is not SURFACE, it
|
||||
// turns on the UBO batching. Otherwise, it turns off the UBO batching.
|
||||
DEFAULT,
|
||||
NO_UBO_BATCHING,
|
||||
UBO_BATCHING
|
||||
};
|
||||
|
||||
FMaterialInstance* createMaterialInstance(const FMaterial* material,
|
||||
const FMaterialInstance* other, const char* name) noexcept;
|
||||
|
||||
FMaterialInstance* createMaterialInstance(const FMaterial* material, const char* name,
|
||||
UboBatchingMode batchingMode) noexcept;
|
||||
FMaterialInstance* createMaterialInstance(const FMaterial* material,
|
||||
const char* name) noexcept;
|
||||
|
||||
FScene* createScene() noexcept;
|
||||
FView* createView() noexcept;
|
||||
@@ -465,7 +446,6 @@ public:
|
||||
|
||||
void prepare();
|
||||
void gc();
|
||||
void submitFrame();
|
||||
|
||||
using ShaderContent = utils::FixedCapacityVector<uint8_t>;
|
||||
|
||||
@@ -670,7 +650,6 @@ private:
|
||||
|
||||
uint32_t mFlushCounter = 0;
|
||||
|
||||
UboManager* mUboManager = nullptr;
|
||||
RootArenaScope::Arena mPerRenderPassArena;
|
||||
HeapAllocatorArena mHeapAllocator;
|
||||
|
||||
@@ -780,7 +759,7 @@ public:
|
||||
bool assert_texture_can_generate_mipmap = CORRECTNESS_ASSERTION_DEFAULT;
|
||||
} debug;
|
||||
struct {
|
||||
bool disable_gpu_frame_complete_metric = true;
|
||||
bool disable_gpu_frame_complete_metric = false;
|
||||
} frame_info;
|
||||
} engine;
|
||||
struct {
|
||||
|
||||
@@ -73,7 +73,6 @@ namespace filament {
|
||||
using namespace backend;
|
||||
using namespace filaflat;
|
||||
using namespace utils;
|
||||
using UboBatchingMode = FEngine::UboBatchingMode;
|
||||
|
||||
struct Material::BuilderDetails {
|
||||
const void* mPayload = nullptr;
|
||||
@@ -218,17 +217,16 @@ void FMaterial::terminate(FEngine& engine) {
|
||||
|
||||
filament::DescriptorSetLayout const& FMaterial::getPerViewDescriptorSetLayout(
|
||||
Variant const variant, bool const useVsmDescriptorSetLayout) const noexcept {
|
||||
if (mDefinition.materialDomain == MaterialDomain::SURFACE) {
|
||||
// `variant` is only sensical for MaterialDomain::SURFACE
|
||||
if (Variant::isValidDepthVariant(variant)) {
|
||||
return mEngine.getPerViewDescriptorSetLayoutDepthVariant();
|
||||
}
|
||||
if (Variant::isSSRVariant(variant)) {
|
||||
return mEngine.getPerViewDescriptorSetLayoutSsrVariant();
|
||||
}
|
||||
if (Variant::isValidDepthVariant(variant)) {
|
||||
assert_invariant(mDefinition.materialDomain == MaterialDomain::SURFACE);
|
||||
return mEngine.getPerViewDescriptorSetLayoutDepthVariant();
|
||||
}
|
||||
if (Variant::isSSRVariant(variant)) {
|
||||
assert_invariant(mDefinition.materialDomain == MaterialDomain::SURFACE);
|
||||
return mEngine.getPerViewDescriptorSetLayoutSsrVariant();
|
||||
}
|
||||
// mDefinition.perViewDescriptorSetLayout{Vsm} is already resolved for MaterialDomain
|
||||
if (useVsmDescriptorSetLayout) {
|
||||
assert_invariant(mDefinition.materialDomain == MaterialDomain::SURFACE);
|
||||
return mDefinition.perViewDescriptorSetLayoutVsm;
|
||||
}
|
||||
return mDefinition.perViewDescriptorSetLayout;
|
||||
@@ -282,14 +280,14 @@ FMaterialInstance* FMaterial::createInstance(const char* name) const noexcept {
|
||||
return FMaterialInstance::duplicate(mDefaultMaterialInstance, name);
|
||||
} else {
|
||||
// but if we don't, just create an instance with all the default parameters
|
||||
return mEngine.createMaterialInstance(this, name, UboBatchingMode::DEFAULT);
|
||||
return mEngine.createMaterialInstance(this, name);
|
||||
}
|
||||
}
|
||||
|
||||
FMaterialInstance* FMaterial::getDefaultInstance() noexcept {
|
||||
if (UTILS_UNLIKELY(!mDefaultMaterialInstance)) {
|
||||
mDefaultMaterialInstance =
|
||||
mEngine.createMaterialInstance(this, mDefinition.name.c_str(), UboBatchingMode::DEFAULT);
|
||||
mDefaultMaterialInstance = mEngine.createMaterialInstance(this,
|
||||
mDefinition.name.c_str());
|
||||
mDefaultMaterialInstance->setDefaultInstance(true);
|
||||
}
|
||||
return mDefaultMaterialInstance;
|
||||
|
||||
@@ -108,7 +108,7 @@ public:
|
||||
backend::CallbackHandler* handler,
|
||||
utils::Invocable<void(Material*)>&& callback) noexcept;
|
||||
|
||||
// Creates an instance of this material, specifying the batching mode.
|
||||
// Create an instance of this material
|
||||
FMaterialInstance* createInstance(const char* name) const noexcept;
|
||||
|
||||
bool hasParameter(const char* name) const noexcept;
|
||||
|
||||
@@ -58,19 +58,15 @@ using namespace utils;
|
||||
namespace filament {
|
||||
|
||||
using namespace backend;
|
||||
using UboBatchingMode = FEngine::UboBatchingMode;
|
||||
|
||||
bool shouldEnableBatching(FEngine& engine, UboBatchingMode batchingMode, MaterialDomain domain) {
|
||||
if (batchingMode != UboBatchingMode::DEFAULT) {
|
||||
return batchingMode == UboBatchingMode::UBO_BATCHING;
|
||||
}
|
||||
|
||||
return engine.isUboBatchingEnabled() && domain == MaterialDomain::SURFACE;
|
||||
FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
|
||||
const char* name) noexcept
|
||||
: FMaterialInstance(engine, material, name,
|
||||
engine.features.material.enable_material_instance_uniform_batching) {
|
||||
}
|
||||
|
||||
FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material, const char* name,
|
||||
UboBatchingMode batchingMode) noexcept
|
||||
: mMaterial(material),
|
||||
FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
|
||||
const char* name, bool useUboBatching) noexcept : mMaterial(material),
|
||||
mDescriptorSet("MaterialInstance", material->getDescriptorSetLayout()),
|
||||
mCulling(CullingMode::BACK),
|
||||
mShadowCulling(CullingMode::BACK),
|
||||
@@ -80,12 +76,10 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
|
||||
mHasScissor(false),
|
||||
mIsDoubleSided(false),
|
||||
mIsDefaultInstance(false),
|
||||
mUseUboBatching(
|
||||
shouldEnableBatching(engine, batchingMode, material->getMaterialDomain())),
|
||||
mUseUboBatching(useUboBatching),
|
||||
mTransparencyMode(TransparencyMode::DEFAULT),
|
||||
mName(name ? CString(name) : material->getName()) {
|
||||
FILAMENT_CHECK_PRECONDITION(!mUseUboBatching || engine.isUboBatchingEnabled())
|
||||
<< "UBO batching is not enabled.";
|
||||
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
|
||||
// even if the material doesn't have any parameters, we allocate a small UBO because it's
|
||||
@@ -159,7 +153,7 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
|
||||
mUseUboBatching(other->mUseUboBatching),
|
||||
mScissorRect(other->mScissorRect),
|
||||
mName(name ? CString(name) : other->mName) {
|
||||
assert_invariant(!mUseUboBatching || engine.isUboBatchingEnabled());
|
||||
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
FMaterial const* const material = other->getMaterial();
|
||||
|
||||
@@ -199,8 +193,8 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
|
||||
}
|
||||
}
|
||||
|
||||
FMaterialInstance* FMaterialInstance::duplicate(FMaterialInstance const* other,
|
||||
const char* name) noexcept {
|
||||
FMaterialInstance* FMaterialInstance::duplicate(
|
||||
FMaterialInstance const* other, const char* name) noexcept {
|
||||
FMaterial const* const material = other->getMaterial();
|
||||
FEngine& engine = material->getEngine();
|
||||
return engine.createMaterialInstance(material, other, name);
|
||||
@@ -244,21 +238,15 @@ void FMaterialInstance::commitStreamUniformAssociations(FEngine::DriverApi& driv
|
||||
|
||||
void FMaterialInstance::commit(FEngine& engine) const {
|
||||
if (UTILS_LIKELY(mMaterial->getMaterialDomain() != MaterialDomain::SURFACE)) {
|
||||
commit(engine.getDriverApi(), engine.getUboManager());
|
||||
commit(engine.getDriverApi());
|
||||
}
|
||||
}
|
||||
|
||||
void FMaterialInstance::commit(FEngine::DriverApi& driver, UboManager* uboManager) const {
|
||||
void FMaterialInstance::commit(FEngine::DriverApi& driver) const {
|
||||
if (mUniforms.isDirty() || mHasStreamUniformAssociations) {
|
||||
mUniforms.clean();
|
||||
if (mUseUboBatching) {
|
||||
assert_invariant(uboManager != nullptr);
|
||||
if (!BufferAllocator::isValid(getAllocationId())) {
|
||||
// The allocation hasn't happened yet, return.
|
||||
return;
|
||||
}
|
||||
|
||||
uboManager->updateSlot(driver, getAllocationId(), mUniforms.toBufferDescriptor(driver));
|
||||
// TODO: update the content by `copyToMemoryMappedBuffer`
|
||||
}
|
||||
else {
|
||||
auto* ubHandle = std::get_if<Handle<HwBufferObject>>(&mUboData);
|
||||
@@ -283,10 +271,6 @@ void FMaterialInstance::commit(FEngine::DriverApi& driver, UboManager* uboManage
|
||||
// TODO: eventually we should remove this in RELEASE builds
|
||||
fixMissingSamplers();
|
||||
|
||||
if (mUseUboBatching && !BufferAllocator::isValid(getAllocationId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Commit descriptors if needed (e.g. when textures are updated,or the first time)
|
||||
mDescriptorSet.commit(mMaterial->getDescriptorSetLayout(), driver);
|
||||
}
|
||||
@@ -429,13 +413,6 @@ const char* FMaterialInstance::getName() const noexcept {
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void FMaterialInstance::use(FEngine::DriverApi& driver, Variant variant) const {
|
||||
if (!mDescriptorSet.getHandle()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mUseUboBatching && !BufferAllocator::isValid(getAllocationId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY(mMissingSamplerDescriptors.any())) {
|
||||
std::call_once(mMissingSamplersFlag, [this] {
|
||||
@@ -462,8 +439,7 @@ void FMaterialInstance::use(FEngine::DriverApi& driver, Variant variant) const {
|
||||
return;
|
||||
}
|
||||
|
||||
mDescriptorSet.bind(driver, DescriptorSetBindingPoints::PER_MATERIAL,
|
||||
{ { mUboOffset }, driver });
|
||||
mDescriptorSet.bind(driver, DescriptorSetBindingPoints::PER_MATERIAL);
|
||||
}
|
||||
|
||||
void FMaterialInstance::assignUboAllocation(
|
||||
@@ -473,10 +449,8 @@ void FMaterialInstance::assignUboAllocation(
|
||||
assert_invariant(mUseUboBatching);
|
||||
|
||||
mUboData = id;
|
||||
mUboOffset = offset;
|
||||
if (BufferAllocator::isValid(id)) {
|
||||
// Use dynamic offset during binding, so the offset here is always zero.
|
||||
mDescriptorSet.setBuffer(mMaterial->getDescriptorSetLayout(), 0, ubHandle, 0,
|
||||
mDescriptorSet.setBuffer(mMaterial->getDescriptorSetLayout(), 0, ubHandle, offset,
|
||||
mUniforms.getSize());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,8 +56,11 @@ class FTexture;
|
||||
|
||||
class FMaterialInstance : public MaterialInstance {
|
||||
public:
|
||||
FMaterialInstance(FEngine& engine, FMaterial const* material, const char* name,
|
||||
FEngine::UboBatchingMode batchingMode) noexcept;
|
||||
FMaterialInstance(FEngine& engine, FMaterial const* material,
|
||||
const char* name) noexcept;
|
||||
// Use this constructor when you need to override the ubo batching flag for an individual MI.
|
||||
FMaterialInstance(FEngine& engine, FMaterial const* material,
|
||||
const char* name, bool useUboBatching) noexcept;
|
||||
FMaterialInstance(FEngine& engine, FMaterialInstance const* other, const char* name);
|
||||
FMaterialInstance(const FMaterialInstance& rhs) = delete;
|
||||
FMaterialInstance& operator=(const FMaterialInstance& rhs) = delete;
|
||||
@@ -72,7 +75,7 @@ public:
|
||||
|
||||
void commit(FEngine& engine) const;
|
||||
|
||||
void commit(FEngine::DriverApi& driver, UboManager* uboManager) const;
|
||||
void commit(FEngine::DriverApi& driver) const;
|
||||
|
||||
void use(FEngine::DriverApi& driver, Variant variant = {}) const;
|
||||
|
||||
@@ -284,7 +287,6 @@ private:
|
||||
};
|
||||
|
||||
std::variant<BufferAllocator::AllocationId, backend::Handle<backend::HwBufferObject>> mUboData;
|
||||
BufferAllocator::allocation_size_t mUboOffset = 0;
|
||||
tsl::robin_map<backend::descriptor_binding_t, TextureParameter> mTextureParameters;
|
||||
mutable DescriptorSet mDescriptorSet;
|
||||
UniformBuffer mUniforms;
|
||||
@@ -306,7 +308,7 @@ private:
|
||||
bool mHasScissor : 1;
|
||||
bool mIsDoubleSided : 1;
|
||||
bool mIsDefaultInstance : 1;
|
||||
const bool mUseUboBatching : 1;
|
||||
bool mUseUboBatching : 1;
|
||||
TransparencyMode mTransparencyMode : 2;
|
||||
|
||||
uint64_t mMaterialSortingKey = 0;
|
||||
|
||||
@@ -452,8 +452,6 @@ void FRenderer::endFrame() {
|
||||
mFrameInfoManager.endFrame(driver);
|
||||
mFrameSkipper.submitFrame(driver);
|
||||
|
||||
engine.submitFrame();
|
||||
|
||||
driver.endFrame(mFrameId);
|
||||
|
||||
// gives the backend a chance to execute periodic tasks
|
||||
@@ -589,8 +587,6 @@ void FRenderer::renderStandaloneView(FView const* view) {
|
||||
// happen with Renderer::beginFrame/endFrame.
|
||||
renderInternal(view, true);
|
||||
|
||||
engine.submitFrame();
|
||||
|
||||
driver.endFrame(mFrameId);
|
||||
|
||||
// engine.flush() has already been called by renderInternal(), but we need an extra one
|
||||
|
||||
@@ -302,7 +302,7 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const Builder& builder)
|
||||
if (!mBufferObjectsEnabled) {
|
||||
// If buffer objects are not enabled at the API level, then we create them internally.
|
||||
#pragma nounroll
|
||||
for (size_t index = 0; index < MAX_VERTEX_ATTRIBUTE_COUNT; ++index) {
|
||||
for (size_t index = 0; index < MAX_VERTEX_BUFFER_COUNT; ++index) {
|
||||
size_t const i = mAttributes[index].buffer;
|
||||
if (i != Attribute::BUFFER_UNUSED) {
|
||||
assert_invariant(bufferSizes[i] > 0);
|
||||
|
||||
@@ -39,7 +39,7 @@ void PostProcessDescriptorSet::init(FEngine& engine) noexcept {
|
||||
// create the descriptor-set layout
|
||||
mDescriptorSetLayout = filament::DescriptorSetLayout{
|
||||
engine.getDescriptorSetLayoutFactory(),
|
||||
engine.getDriverApi(), descriptor_sets::getDepthVariantLayout() };
|
||||
engine.getDriverApi(), descriptor_sets::getPostProcessLayout() };
|
||||
|
||||
// create the descriptor-set from the layout
|
||||
mDescriptorSet = DescriptorSet{ "PostProcessDescriptorSet", mDescriptorSetLayout };
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "Filament"
|
||||
spec.version = "1.68.0"
|
||||
spec.version = "1.67.0"
|
||||
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
|
||||
spec.homepage = "https://google.github.io/filament"
|
||||
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.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.0/filament-v1.67.0-ios.tgz" }
|
||||
|
||||
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
|
||||
spec.pod_target_xcconfig = {
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
namespace filament {
|
||||
|
||||
// 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
|
||||
|
||||
@@ -28,9 +28,9 @@
|
||||
|
||||
namespace filament::descriptor_sets {
|
||||
|
||||
backend::DescriptorSetLayout const& getPostProcessLayout() noexcept;
|
||||
backend::DescriptorSetLayout const& getDepthVariantLayout() noexcept;
|
||||
backend::DescriptorSetLayout const& getSsrVariantLayout() noexcept;
|
||||
|
||||
backend::DescriptorSetLayout const& getPerRenderableLayout() noexcept;
|
||||
|
||||
backend::DescriptorSetLayout getPerViewDescriptorSetLayout(
|
||||
|
||||
@@ -40,7 +40,12 @@ namespace filament::descriptor_sets {
|
||||
|
||||
using namespace backend;
|
||||
|
||||
// used to generate shadow-maps, structure and postfx passes
|
||||
// used for post-processing passes
|
||||
static constexpr std::initializer_list<DescriptorSetLayoutBinding> postProcessDescriptorSetLayoutList = {
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
|
||||
};
|
||||
|
||||
// used to generate shadow-maps
|
||||
static constexpr std::initializer_list<DescriptorSetLayoutBinding> depthVariantDescriptorSetLayoutList = {
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerViewBindingPoints::FRAME_UNIFORMS },
|
||||
};
|
||||
@@ -137,6 +142,10 @@ static const std::unordered_map<
|
||||
{{ SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT }, DescriptorType::SAMPLER_EXTERNAL }
|
||||
};
|
||||
|
||||
// used for post-processing passes
|
||||
static DescriptorSetLayout const postProcessDescriptorSetLayout{ utils::StaticString("postProcess"),
|
||||
postProcessDescriptorSetLayoutList };
|
||||
|
||||
// used to generate shadow-maps
|
||||
static DescriptorSetLayout const depthVariantDescriptorSetLayout{
|
||||
utils::StaticString("depthVariant"), depthVariantDescriptorSetLayoutList
|
||||
@@ -154,6 +163,10 @@ static DescriptorSetLayout perRenderableDescriptorSetLayout = {
|
||||
utils::StaticString("perRenderable"), perRenderableDescriptorSetLayoutList
|
||||
};
|
||||
|
||||
DescriptorSetLayout const& getPostProcessLayout() noexcept {
|
||||
return postProcessDescriptorSetLayout;
|
||||
}
|
||||
|
||||
DescriptorSetLayout const& getDepthVariantLayout() noexcept {
|
||||
return depthVariantDescriptorSetLayout;
|
||||
}
|
||||
@@ -267,10 +280,10 @@ DescriptorSetLayout getPerViewDescriptorSetLayout(
|
||||
return layout;
|
||||
}
|
||||
case MaterialDomain::POST_PROCESS:
|
||||
return depthVariantDescriptorSetLayout;
|
||||
return postProcessDescriptorSetLayout;
|
||||
case MaterialDomain::COMPUTE:
|
||||
// TODO: what's the layout for compute?
|
||||
return depthVariantDescriptorSetLayout;
|
||||
return postProcessDescriptorSetLayout;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -190,47 +190,6 @@ void ImGuiHelper::render(float timeStepInSeconds, Callback imguiCommands) {
|
||||
void ImGuiHelper::processImGuiCommands(ImDrawData* commands, const ImGuiIO& io) {
|
||||
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;
|
||||
auto& rcm = mEngine->getRenderableManager();
|
||||
|
||||
@@ -269,7 +228,7 @@ void ImGuiHelper::processImGuiCommands(ImDrawData* commands, const ImGuiIO& io)
|
||||
if (pcmd.UserCallback) {
|
||||
pcmd.UserCallback(cmds, &pcmd);
|
||||
} else {
|
||||
auto texture = (Texture const*)pcmd.GetTexID();
|
||||
auto texture = (Texture const*)pcmd.TextureId;
|
||||
MaterialInstance* materialInstance;
|
||||
#ifdef __ANDROID__
|
||||
if (texture && texture->getTarget() == Texture::Sampler::SAMPLER_EXTERNAL) {
|
||||
|
||||
@@ -129,9 +129,9 @@ DescriptorSetLayout getPerMaterialDescriptorSet(SamplerInterfaceBlock const& sib
|
||||
DescriptorSetLayout layout;
|
||||
layout.bindings.reserve(1 + samplers.size());
|
||||
|
||||
layout.bindings.push_back(DescriptorSetLayoutBinding{ DescriptorType::UNIFORM_BUFFER,
|
||||
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT,
|
||||
+PerMaterialBindingPoints::MATERIAL_PARAMS, DescriptorFlags::DYNAMIC_OFFSET, 0 });
|
||||
layout.bindings.push_back(DescriptorSetLayoutBinding { DescriptorType::UNIFORM_BUFFER,
|
||||
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT,
|
||||
+PerMaterialBindingPoints::MATERIAL_PARAMS, DescriptorFlags::NONE, 0 });
|
||||
|
||||
for (auto const& sampler: samplers) {
|
||||
DescriptorSetLayoutBinding layoutBinding{
|
||||
|
||||
@@ -225,7 +225,7 @@ void MaterialDescriptorSetLayoutChunk::flatten(Flattener& f) {
|
||||
f.writeUint8(uint8_t(DescriptorType::UNIFORM_BUFFER));
|
||||
f.writeUint8(uint8_t(ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT));
|
||||
f.writeUint8(0);
|
||||
f.writeUint8(uint8_t(DescriptorFlags::DYNAMIC_OFFSET));
|
||||
f.writeUint8(uint8_t(DescriptorFlags::NONE));
|
||||
f.writeUint16(0);
|
||||
|
||||
// all the material's sampler descriptors
|
||||
|
||||
@@ -37,13 +37,9 @@ set(SRCS
|
||||
src/IcoSphere.cpp
|
||||
src/MeshAssimp.cpp
|
||||
src/Sphere.cpp
|
||||
src/KeyInputConversion.h
|
||||
src/VulkanPlatformHelper.cpp
|
||||
)
|
||||
|
||||
if (FILAMENT_SUPPORTS_VULKAN)
|
||||
list(APPEND SRCS src/VulkanPlatformHelper.cpp)
|
||||
endif()
|
||||
|
||||
set(LIBS
|
||||
assimp
|
||||
camutils
|
||||
@@ -83,11 +79,7 @@ endif()
|
||||
|
||||
if (WIN32)
|
||||
list(APPEND SRCS src/NativeWindowHelperWindows.cpp)
|
||||
|
||||
if (FILAMENT_SUPPORTS_VULKAN)
|
||||
list(APPEND SRCS src/VulkanPlatformHelperWindows.cpp)
|
||||
endif()
|
||||
|
||||
list(APPEND SRCS src/VulkanPlatformHelperWindows.cpp)
|
||||
list(APPEND LIBS sdl2main)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
#include <filamentapp/FilamentApp.h>
|
||||
|
||||
#include "KeyInputConversion.h"
|
||||
|
||||
#if defined(WIN32)
|
||||
# include <SDL_syswm.h>
|
||||
# include <utils/unwindows.h>
|
||||
@@ -213,6 +211,27 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
|
||||
SDL_GetWindowWMInfo(window->getSDLWindow(), &wmInfo);
|
||||
ImGui::GetMainViewport()->PlatformHandleRaw = wmInfo.info.win.window;
|
||||
#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) {
|
||||
SDL_SetClipboardText(text);
|
||||
};
|
||||
@@ -286,19 +305,15 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback,
|
||||
io.AddInputCharactersUTF8(event->text.text);
|
||||
break;
|
||||
}
|
||||
case SDL_KEYUP:
|
||||
case SDL_KEYDOWN: {
|
||||
SDL_Scancode const scancode = event->key.keysym.scancode;
|
||||
SDL_Keycode const keycode = event->key.keysym.sym;
|
||||
|
||||
auto modState = SDL_GetModState();
|
||||
io.AddKeyEvent(ImGuiMod_Ctrl, (modState & KMOD_CTRL) != 0);
|
||||
io.AddKeyEvent(ImGuiMod_Shift, (modState & KMOD_SHIFT) != 0);
|
||||
io.AddKeyEvent(ImGuiMod_Alt, (modState & KMOD_ALT) != 0);
|
||||
io.AddKeyEvent(ImGuiMod_Super, (modState & KMOD_GUI) != 0);
|
||||
io.AddKeyEvent(
|
||||
filamentapp_utils::ImGui_ImplSDL2_KeyEventToImGuiKey(keycode, scancode),
|
||||
event->type == SDL_KEYDOWN);
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP: {
|
||||
int key = event->key.keysym.scancode;
|
||||
IM_ASSERT(key >= 0 && key < IM_ARRAYSIZE(io.KeysDown));
|
||||
io.KeysDown[key] = (event->type == SDL_KEYDOWN);
|
||||
io.KeyShift = ((SDL_GetModState() & KMOD_SHIFT) != 0);
|
||||
io.KeyAlt = ((SDL_GetModState() & KMOD_ALT) != 0);
|
||||
io.KeyCtrl = ((SDL_GetModState() & KMOD_CTRL) != 0);
|
||||
io.KeySuper = ((SDL_GetModState() & KMOD_GUI) != 0);
|
||||
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
|
||||
@@ -281,7 +281,7 @@ static bool printParametersInfo(ostream& text, const ChunkContainer& container)
|
||||
}
|
||||
|
||||
text << " "
|
||||
<< setw(alignment) << fieldName.c_str_safe()
|
||||
<< setw(alignment) << fieldName.c_str()
|
||||
<< setw(shortAlignment) << toString(UniformType(fieldType))
|
||||
<< arraySizeToString(fieldSize)
|
||||
<< setw(shortAlignment) << toString(Precision(fieldPrecision))
|
||||
@@ -325,7 +325,7 @@ static bool printParametersInfo(ostream& text, const ChunkContainer& container)
|
||||
}
|
||||
|
||||
text << " "
|
||||
<< setw(alignment) << fieldName.c_str_safe()
|
||||
<< setw(alignment) << fieldName.c_str()
|
||||
<< setw(shortAlignment) << +fieldBinding
|
||||
<< setw(shortAlignment) << toString(SamplerType(fieldType))
|
||||
<< setw(shortAlignment) << toString(Precision(fieldPrecision))
|
||||
@@ -364,7 +364,7 @@ static bool printConstantInfo(ostream& text, const ChunkContainer& container) {
|
||||
}
|
||||
|
||||
text << " "
|
||||
<< setw(alignment) << fieldName.c_str_safe()
|
||||
<< setw(alignment) << fieldName.c_str()
|
||||
<< setw(shortAlignment) << toString(ConstantType(fieldType))
|
||||
<< endl;
|
||||
}
|
||||
|
||||
@@ -56,9 +56,6 @@ public:
|
||||
//! Returns true if the map is full.
|
||||
bool full() const noexcept { return mSize == N; }
|
||||
|
||||
//! Clears the map entirely.
|
||||
void clear() noexcept { mSize = 0; mHead = 0; }
|
||||
|
||||
/**
|
||||
* Inserts a new key-value pair.
|
||||
* The key must be greater than the key of the last inserted element.
|
||||
@@ -68,7 +65,7 @@ public:
|
||||
*/
|
||||
UTILS_NOINLINE void insert(key_type key, mapped_type value) {
|
||||
assert(empty() || key > back().first); // assert monotonic
|
||||
if (UTILS_LIKELY(full())) {
|
||||
if (full()) {
|
||||
// container is full, replace the oldest element
|
||||
mStorage[mHead] = { key, value };
|
||||
mHead = (mHead + 1) % N;
|
||||
|
||||
@@ -55,10 +55,7 @@ AsyncJobQueue::AsyncJobQueue(const char* name, Priority priority) {
|
||||
}
|
||||
|
||||
AsyncJobQueue::~AsyncJobQueue() noexcept {
|
||||
// wait for all pending callbacks to be called & terminate the thread
|
||||
drainAndExit();
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
assert_invariant(!mThread.joinable());
|
||||
assert_invariant(mQueue.empty());
|
||||
#endif
|
||||
}
|
||||
@@ -97,9 +94,6 @@ bool AsyncJobQueue::isValid() const noexcept {
|
||||
void AsyncJobQueue::drainAndExit() {
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
std::unique_lock lock(mLock);
|
||||
if (mExitRequested) {
|
||||
return;
|
||||
}
|
||||
// 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,
|
||||
// no new jobs can be added, so we can join the thread.
|
||||
|
||||
@@ -41,36 +41,6 @@ using namespace filament::math;
|
||||
namespace filament {
|
||||
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) {
|
||||
float3 minpt = bounds.min;
|
||||
float3 maxpt = bounds.max;
|
||||
@@ -593,6 +563,27 @@ void ViewerGui::renderUserInterface(float timeStepInSeconds, View* guiView, floa
|
||||
mImGuiHelper = new ImGuiHelper(mEngine, guiView, "");
|
||||
|
||||
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
|
||||
// proggy font. Users need to refresh their window when dragging between displays with
|
||||
// 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[1] = false;
|
||||
io.MouseDown[2] = false;
|
||||
io.AddKeyEvent(ImGuiMod_Ctrl, control);
|
||||
io.KeyCtrl = control;
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerGui::keyDownEvent(int keyCode) {
|
||||
if (!mImGuiHelper) {
|
||||
return;
|
||||
if (mImGuiHelper && keyCode < IM_ARRAYSIZE(ImGui::GetIO().KeysDown)) {
|
||||
ImGui::GetIO().KeysDown[keyCode] = true;
|
||||
}
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.AddKeyEvent(
|
||||
convertKey(keyCode),
|
||||
true);
|
||||
}
|
||||
|
||||
void ViewerGui::keyUpEvent(int keyCode) {
|
||||
if (!mImGuiHelper) {
|
||||
return;
|
||||
if (mImGuiHelper && keyCode < IM_ARRAYSIZE(ImGui::GetIO().KeysDown)) {
|
||||
ImGui::GetIO().KeysDown[keyCode] = false;
|
||||
}
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.AddKeyEvent(
|
||||
convertKey(keyCode),
|
||||
false);
|
||||
}
|
||||
|
||||
void ViewerGui::keyPressEvent(int charCode) {
|
||||
|
||||
@@ -302,16 +302,14 @@ vec4 evaluateLights(const MaterialInputs material) {
|
||||
return vec4(color, computeDiffuseAlpha(material.baseColor.a));
|
||||
}
|
||||
|
||||
vec3 emissive(const MaterialInputs material, float alpha) {
|
||||
void addEmissive(const MaterialInputs material, inout vec4 color) {
|
||||
#if defined(MATERIAL_HAS_EMISSIVE)
|
||||
highp vec4 emissive = material.emissive;
|
||||
highp float attenuation = mix(1.0, getExposure(), emissive.w);
|
||||
#if defined(BLEND_MODE_TRANSPARENT) || defined(BLEND_MODE_FADE)
|
||||
attenuation *= alpha;
|
||||
attenuation *= color.a;
|
||||
#endif
|
||||
return emissive.rgb * attenuation;
|
||||
#else
|
||||
return vec3(0.0);
|
||||
color.rgb += emissive.rgb * attenuation;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -323,7 +321,7 @@ vec3 emissive(const MaterialInputs material, float alpha) {
|
||||
*/
|
||||
vec4 evaluateMaterial(const MaterialInputs material) {
|
||||
vec4 color = evaluateLights(material);
|
||||
color.rgb += emissive(material, color.a);
|
||||
addEmissive(material, color);
|
||||
// kill +inf generated by strong highlights
|
||||
color.rgb = min(color.rgb, MEDIUMP_FLT_MAX);
|
||||
return color;
|
||||
|
||||
@@ -4,12 +4,10 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
FOR FIRST-TIME USERS ISSUES COMPILING/LINKING/RUNNING, please use [GitHub Discussions](https://github.com/ocornut/imgui/discussions)
|
||||
For anything else: **we are happy to use 'GitHub Issues' for many types of open-ended questions**. We are encouraging 'Issues' becoming a large, centralized, tagged, cross-referenced database of Dear ImGui contents.
|
||||
FOR FIRST-TIME USERS ISSUES COMPILING/LINKING/RUNNING or LOADING FONTS, please use [GitHub Discussions](https://github.com/ocornut/imgui/discussions)
|
||||
For anything else: we are happy to use 'GitHub Issues' for many types of open-ended questions. We are encouraging 'Issues' becoming a large, centralized and cross-referenced database of Dear ImGui contents.
|
||||
|
||||
Be mindful that messages are being sent to the e-mail box of "Watching" users. Try to proof-read your messages before sending them. Edits are not seen by those users.
|
||||
|
||||
**If you are using Dear ImGui as part of a job that you are being well-paid for** and your company is not a sponsor. Please be mindful that this is a Free Software and you might be about to ask volunteers to help you doing your job. Please put extra effort describing your issue or question properly. If your company is wealthy, please read [Funding](https://github.com/ocornut/imgui/wiki/Funding) and consider getting in touch.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
|
||||
+1
-5
@@ -2,9 +2,5 @@
|
||||
|
||||
1. PLEASE CAREFULLY READ: [Contributing Guidelines](https://github.com/ocornut/imgui/blob/master/docs/CONTRIBUTING.md)
|
||||
|
||||
2. **Make sure you're using a special branch just for this pull request**. (In git, 1 PR = 1 branch. If you update the branch the PR will be updated.)
|
||||
|
||||
3. Consider running the [imgui_test_suite](https://github.com/ocornut/imgui_test_engine) or adding new tests to test for expected behaviors.
|
||||
|
||||
4. Clear this template before submitting your PR.
|
||||
2. Clear this template before submitting your PR.
|
||||
|
||||
|
||||
+55
-322
@@ -8,7 +8,6 @@ on:
|
||||
# "scheduled" workflow, while maintaining ability to perform local CI builds.
|
||||
workflows:
|
||||
- scheduled
|
||||
- manual
|
||||
branches:
|
||||
- master
|
||||
- docking
|
||||
@@ -16,46 +15,32 @@ on:
|
||||
- requested
|
||||
|
||||
jobs:
|
||||
Build-Windows:
|
||||
runs-on: windows-2025
|
||||
name: Build - Windows
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ github.workspace }}/imgui
|
||||
|
||||
Windows:
|
||||
runs-on: windows-2019
|
||||
env:
|
||||
VS_PATH: C:\Program Files\Microsoft Visual Studio\2022\Enterprise\
|
||||
MSBUILD_PATH: C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\
|
||||
VS_PATH: C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\
|
||||
MSBUILD_PATH: C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
path: ${{ github.workspace }}/imgui
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# The VulkanSDK libs for Windows is manually generated using build_windows_vulkan_libs.ps1 + attached to issue #8925.
|
||||
# (we have a .yml workflow in commit history if it becomes ever useful to create this on CI too)
|
||||
- name: Install Dependencies
|
||||
shell: powershell
|
||||
run: |
|
||||
Invoke-WebRequest -Uri "https://www.libsdl.org/release/SDL2-devel-2.32.8-VC.zip" -OutFile "SDL2-devel-2.32.8-VC.zip"
|
||||
Expand-Archive -Path SDL2-devel-2.32.8-VC.zip
|
||||
echo "SDL2_DIR=$(pwd)\SDL2-devel-2.32.8-VC\SDL2-2.32.8\" >>${env:GITHUB_ENV}
|
||||
Invoke-WebRequest -Uri "https://www.libsdl.org/release/SDL2-devel-2.26.3-VC.zip" -OutFile "SDL2-devel-2.26.3-VC.zip"
|
||||
Expand-Archive -Path SDL2-devel-2.26.3-VC.zip
|
||||
echo "SDL2_DIR=$(pwd)\SDL2-devel-2.26.3-VC\SDL2-2.26.3\" >>${env:GITHUB_ENV}
|
||||
|
||||
Invoke-WebRequest -Uri "https://www.libsdl.org/release/SDL3-devel-3.2.18-VC.zip" -OutFile "SDL3-devel-3.2.18-VC.zip"
|
||||
Expand-Archive -Path SDL3-devel-3.2.18-VC.zip
|
||||
echo "SDL3_DIR=$(pwd)\SDL3-devel-3.2.18-VC\SDL3-3.2.18\" >>${env:GITHUB_ENV}
|
||||
|
||||
Invoke-WebRequest -Uri "https://github.com/user-attachments/files/22464296/vulkan_windows_libs_1.4.326.zip" -OutFile vulkan_windows_libs_1.4.326.zip
|
||||
Expand-Archive -Path vulkan_windows_libs_1.4.326.zip
|
||||
echo "VULKAN_SDK=$(pwd)\vulkan_windows_libs_1.4.326\" >>${env:GITHUB_ENV}
|
||||
Invoke-WebRequest -Uri "https://github.com/ocornut/imgui/files/3789205/vulkan-sdk-1.1.121.2.zip" -OutFile vulkan-sdk-1.1.121.2.zip
|
||||
Expand-Archive -Path vulkan-sdk-1.1.121.2.zip
|
||||
echo "VULKAN_SDK=$(pwd)\vulkan-sdk-1.1.121.2\" >>${env:GITHUB_ENV}
|
||||
|
||||
- name: Fix Projects
|
||||
shell: powershell
|
||||
run: |
|
||||
# CI workers do not supporter older Visual Studio versions. Fix projects to target newer available version.
|
||||
gci -recurse -filter "*.vcxproj" | ForEach-Object {
|
||||
(Get-Content $_.FullName) -Replace "<PlatformToolset>v\d{3}</PlatformToolset>","<PlatformToolset>v143</PlatformToolset>" | Set-Content -Path $_.FullName
|
||||
(Get-Content $_.FullName) -Replace "<WindowsTargetPlatformVersion>[\d\.]+</WindowsTargetPlatformVersion>",'<WindowsTargetPlatformVersion>$(LatestTargetPlatformVersion)</WindowsTargetPlatformVersion>' | Set-Content -Path $_.FullName
|
||||
(Get-Content $_.FullName) -Replace "<PlatformToolset>v\d{3}</PlatformToolset>","<PlatformToolset>v142</PlatformToolset>" | Set-Content -Path $_.FullName
|
||||
(Get-Content $_.FullName) -Replace "<WindowsTargetPlatformVersion>[\d\.]+</WindowsTargetPlatformVersion>","<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>" | Set-Content -Path $_.FullName
|
||||
}
|
||||
|
||||
# Not using matrix here because it would inflate job count too much. Check out and setup is done for every job and that makes build times way too long.
|
||||
@@ -65,11 +50,15 @@ jobs:
|
||||
- name: Build example_null (mingw 64-bit, as DLL)
|
||||
shell: bash
|
||||
run: |
|
||||
echo '#define IMGUI_API __declspec(dllexport)' > example_single_file.cpp
|
||||
echo '#ifdef _EXPORT' > example_single_file.cpp
|
||||
echo '# define IMGUI_API __declspec(dllexport)' >> example_single_file.cpp
|
||||
echo '#else' >> example_single_file.cpp
|
||||
echo '# define IMGUI_API __declspec(dllimport)' >> example_single_file.cpp
|
||||
echo '#endif' >> example_single_file.cpp
|
||||
echo '#define IMGUI_IMPLEMENTATION' >> example_single_file.cpp
|
||||
echo '#include "misc/single_file/imgui_single_file.h"' >> example_single_file.cpp
|
||||
g++ -I. -Wall -Wformat -shared -o libimgui.dll -Wl,--out-implib,libimgui.a example_single_file.cpp -limm32
|
||||
g++ -I. -Wall -Wformat -DIMGUI_API='__declspec(dllimport)' -DIMGUI_IMPL_API= -o example_null.exe examples/example_null/main.cpp -L. -limgui
|
||||
g++ -I. -Wall -Wformat -D_EXPORT -shared -o libimgui.dll -Wl,--out-implib,libimgui.a example_single_file.cpp -limm32
|
||||
g++ -I. -Wall -Wformat -o example_null.exe examples/example_null/main.cpp -L. -limgui
|
||||
rm -f example_null.exe libimgui.* example_single_file.*
|
||||
|
||||
- name: Build example_null (extra warnings, msvc 64-bit)
|
||||
@@ -109,19 +98,20 @@ jobs:
|
||||
run: |
|
||||
call "%VS_PATH%\VC\Auxiliary\Build\vcvars64.bat"
|
||||
|
||||
echo #define IMGUI_API __declspec(dllexport) > example_single_file.cpp
|
||||
echo #ifdef _EXPORT > example_single_file.cpp
|
||||
echo # define IMGUI_API __declspec(dllexport) >> example_single_file.cpp
|
||||
echo #else >> example_single_file.cpp
|
||||
echo # define IMGUI_API __declspec(dllimport) >> example_single_file.cpp
|
||||
echo #endif >> example_single_file.cpp
|
||||
echo #define IMGUI_IMPLEMENTATION >> example_single_file.cpp
|
||||
echo #include "misc/single_file/imgui_single_file.h" >> example_single_file.cpp
|
||||
|
||||
cl.exe /D_USRDLL /D_WINDLL /I. example_single_file.cpp /LD /FeImGui.dll /link
|
||||
cl.exe /DIMGUI_API=__declspec(dllimport) -DIMGUI_IMPL_API= /I. ImGui.lib /Feexample_null.exe examples/example_null/main.cpp
|
||||
cl.exe /D_USRDLL /D_WINDLL /D_EXPORT /I. example_single_file.cpp /LD /FeImGui.dll /link
|
||||
cl.exe /I. ImGui.lib /Feexample_null.exe examples/example_null/main.cpp
|
||||
|
||||
# Win64 examples are more frequently compilted than the Win32 examples.
|
||||
# More of the Win32 examples requires 'workflow_run' to reduce waste.
|
||||
- name: Build Win32 example_glfw_opengl2
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_glfw_opengl2/example_glfw_opengl2.vcxproj /p:Platform=Win32 /p:Configuration=Release'
|
||||
if: github.event_name == 'workflow_run'
|
||||
|
||||
- name: Build Win32 example_glfw_opengl3
|
||||
shell: cmd
|
||||
@@ -133,11 +123,6 @@ jobs:
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_glfw_vulkan/example_glfw_vulkan.vcxproj /p:Platform=Win32 /p:Configuration=Release'
|
||||
if: github.event_name == 'workflow_run'
|
||||
|
||||
- name: Build Win32 example_sdl2_sdlrenderer2
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_sdlrenderer2/example_sdl2_sdlrenderer2.vcxproj /p:Platform=Win32 /p:Configuration=Release'
|
||||
if: github.event_name == 'workflow_run'
|
||||
|
||||
- name: Build Win32 example_sdl2_vulkan
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_vulkan/example_sdl2_vulkan.vcxproj /p:Platform=Win32 /p:Configuration=Release'
|
||||
@@ -157,126 +142,74 @@ jobs:
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_directx11/example_sdl2_directx11.vcxproj /p:Platform=Win32 /p:Configuration=Release'
|
||||
if: github.event_name == 'workflow_run'
|
||||
|
||||
- name: Build Win32 example_sdl3_opengl3
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl3_opengl3/example_sdl3_opengl3.vcxproj /p:Platform=Win32 /p:Configuration=Release'
|
||||
|
||||
- name: Build Win32 example_sdl3_sdlgpu3
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl3_sdlgpu3/example_sdl3_sdlgpu3.vcxproj /p:Platform=Win32 /p:Configuration=Release'
|
||||
if: github.event_name == 'workflow_run'
|
||||
|
||||
- name: Build Win32 example_sdl3_sdlrenderer3
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl3_sdlrenderer3/example_sdl3_sdlrenderer3.vcxproj /p:Platform=Win32 /p:Configuration=Release'
|
||||
if: github.event_name == 'workflow_run'
|
||||
|
||||
- name: Build Win32 example_sdl3_vulkan
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl3_vulkan/example_sdl3_vulkan.vcxproj /p:Platform=Win32 /p:Configuration=Release'
|
||||
if: github.event_name == 'workflow_run'
|
||||
|
||||
- name: Build Win32 example_win32_directx9
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_win32_directx9/example_win32_directx9.vcxproj /p:Platform=Win32 /p:Configuration=Release'
|
||||
if: github.event_name == 'workflow_run'
|
||||
|
||||
- name: Build Win32 example_win32_directx10
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_win32_directx10/example_win32_directx10.vcxproj /p:Platform=Win32 /p:Configuration=Release'
|
||||
if: github.event_name == 'workflow_run'
|
||||
|
||||
- name: Build Win32 example_win32_directx11
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_win32_directx11/example_win32_directx11.vcxproj /p:Platform=Win32 /p:Configuration=Release'
|
||||
if: github.event_name == 'workflow_run'
|
||||
|
||||
# Windows 64-bits
|
||||
- name: Build Win64 example_glfw_opengl2
|
||||
- name: Build x64 example_glfw_opengl2
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_glfw_opengl2/example_glfw_opengl2.vcxproj /p:Platform=x64 /p:Configuration=Release'
|
||||
if: github.event_name == 'workflow_run'
|
||||
|
||||
- name: Build Win64 example_glfw_opengl3
|
||||
- name: Build x64 example_glfw_opengl3
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_glfw_opengl3/example_glfw_opengl3.vcxproj /p:Platform=x64 /p:Configuration=Release'
|
||||
|
||||
- name: Build Win64 example_glfw_vulkan
|
||||
- name: Build x64 example_glfw_vulkan
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_glfw_vulkan/example_glfw_vulkan.vcxproj /p:Platform=x64 /p:Configuration=Release'
|
||||
|
||||
- name: Build Win64 example_sdl2_sdlrenderer2
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_sdlrenderer2/example_sdl2_sdlrenderer2.vcxproj /p:Platform=x64 /p:Configuration=Release'
|
||||
if: github.event_name == 'workflow_run'
|
||||
|
||||
- name: Build Win64 example_sdl2_vulkan
|
||||
- name: Build x64 example_sdl2_vulkan
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_vulkan/example_sdl2_vulkan.vcxproj /p:Platform=x64 /p:Configuration=Release'
|
||||
if: github.event_name == 'workflow_run'
|
||||
|
||||
- name: Build Win64 example_sdl2_opengl2
|
||||
- name: Build x64 example_sdl2_opengl2
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_opengl2/example_sdl2_opengl2.vcxproj /p:Platform=x64 /p:Configuration=Release'
|
||||
if: github.event_name == 'workflow_run'
|
||||
|
||||
- name: Build Win64 example_sdl2_opengl3
|
||||
- name: Build x64 example_sdl2_opengl3
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_opengl3/example_sdl2_opengl3.vcxproj /p:Platform=x64 /p:Configuration=Release'
|
||||
if: github.event_name == 'workflow_run'
|
||||
|
||||
- name: Build Win64 example_sdl2_directx11
|
||||
- name: Build x64 example_sdl2_directx11
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl2_directx11/example_sdl2_directx11.vcxproj /p:Platform=x64 /p:Configuration=Release'
|
||||
|
||||
- name: Build Win64 example_sdl3_opengl3
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl3_opengl3/example_sdl3_opengl3.vcxproj /p:Platform=x64 /p:Configuration=Release'
|
||||
if: github.event_name == 'workflow_run'
|
||||
|
||||
- name: Build Win64 example_sdl3_sdlgpu3
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl3_sdlgpu3/example_sdl3_sdlgpu3.vcxproj /p:Platform=x64 /p:Configuration=Release'
|
||||
|
||||
- name: Build Win64 example_sdl3_sdlrenderer3
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl3_sdlrenderer3/example_sdl3_sdlrenderer3.vcxproj /p:Platform=x64 /p:Configuration=Release'
|
||||
|
||||
- name: Build Win64 example_sdl3_vulkan
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_sdl3_vulkan/example_sdl3_vulkan.vcxproj /p:Platform=x64 /p:Configuration=Release'
|
||||
if: github.event_name == 'workflow_run'
|
||||
|
||||
- name: Build Win64 example_win32_directx9
|
||||
- name: Build x64 example_win32_directx9
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_win32_directx9/example_win32_directx9.vcxproj /p:Platform=x64 /p:Configuration=Release'
|
||||
if: github.event_name == 'workflow_run'
|
||||
|
||||
- name: Build Win64 example_win32_directx10
|
||||
- name: Build x64 example_win32_directx10
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_win32_directx10/example_win32_directx10.vcxproj /p:Platform=x64 /p:Configuration=Release'
|
||||
if: github.event_name == 'workflow_run'
|
||||
|
||||
- name: Build Win64 example_win32_directx11
|
||||
- name: Build x64 example_win32_directx11
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_win32_directx11/example_win32_directx11.vcxproj /p:Platform=x64 /p:Configuration=Release'
|
||||
if: github.event_name == 'workflow_run'
|
||||
|
||||
- name: Build Win64 example_win32_directx12
|
||||
- name: Build x64 example_win32_directx12
|
||||
shell: cmd
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" examples/example_win32_directx12/example_win32_directx12.vcxproj /p:Platform=x64 /p:Configuration=Release'
|
||||
|
||||
Build-Linux:
|
||||
runs-on: ubuntu-latest
|
||||
name: Build - Linux
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ github.workspace }}/imgui
|
||||
|
||||
Linux:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
path: ${{ github.workspace }}/imgui
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
@@ -380,18 +313,6 @@ jobs:
|
||||
EOF
|
||||
g++ -I. -std=c++11 -Wall -Wformat -o example_single_file example_single_file.cpp
|
||||
|
||||
- name: Build example_null (with C++20)
|
||||
run: |
|
||||
cat > example_single_file.cpp <<'EOF'
|
||||
|
||||
#define IMGUI_DISABLE_OBSOLETE_KEYIO
|
||||
#define IMGUI_IMPLEMENTATION
|
||||
#include "misc/single_file/imgui_single_file.h"
|
||||
#include "examples/example_null/main.cpp"
|
||||
|
||||
EOF
|
||||
g++ -I. -std=c++20 -Wall -Wformat -o example_single_file example_single_file.cpp
|
||||
|
||||
- name: Build example_null (with IMGUI_DISABLE_DEMO_WINDOWS and IMGUI_DISABLE_DEBUG_TOOLS)
|
||||
run: |
|
||||
cat > example_single_file.cpp <<'EOF'
|
||||
@@ -449,18 +370,6 @@ jobs:
|
||||
EOF
|
||||
g++ -I. -std=c++11 -Wall -Wformat -o example_single_file example_single_file.cpp
|
||||
|
||||
- name: Build example_null (C++26, Clang)
|
||||
run: |
|
||||
cat > example_single_file.cpp <<'EOF'
|
||||
|
||||
#define IMGUI_IMPLEMENTATION
|
||||
#define IMGUI_DISABLE_DEMO_WINDOWS
|
||||
#include "misc/single_file/imgui_single_file.h"
|
||||
#include "examples/example_null/main.cpp"
|
||||
|
||||
EOF
|
||||
clang++ -I. -std=c++26 -Wall -Wformat -fno-exceptions -fno-threadsafe-statics -lc -lm -o example_single_file example_single_file.cpp
|
||||
|
||||
- name: Build example_null (without c++ runtime, Clang)
|
||||
run: |
|
||||
cat > example_single_file.cpp <<'EOF'
|
||||
@@ -490,22 +399,14 @@ jobs:
|
||||
- name: Build with IMGUI_IMPL_VULKAN_NO_PROTOTYPES
|
||||
run: g++ -c -I. -std=c++11 -DIMGUI_IMPL_VULKAN_NO_PROTOTYPES=1 backends/imgui_impl_vulkan.cpp
|
||||
|
||||
Build-MacOS:
|
||||
MacOS:
|
||||
runs-on: macos-latest
|
||||
name: Build - MacOS
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ github.workspace }}/imgui
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
path: ${{ github.workspace }}/imgui
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
brew install glfw3 sdl2 sdl3
|
||||
brew install glfw3 sdl2
|
||||
|
||||
- name: Build example_null (extra warnings, clang 64-bit)
|
||||
run: make -C examples/example_null WITH_EXTRA_WARNINGS=1
|
||||
@@ -521,17 +422,6 @@ jobs:
|
||||
EOF
|
||||
clang++ -I. -std=c++11 -Wall -Wformat -o example_single_file example_single_file.cpp
|
||||
|
||||
- name: Build example_null (single file build, c++20)
|
||||
run: |
|
||||
cat > example_single_file.cpp <<'EOF'
|
||||
|
||||
#define IMGUI_IMPLEMENTATION
|
||||
#include "misc/single_file/imgui_single_file.h"
|
||||
#include "examples/example_null/main.cpp"
|
||||
|
||||
EOF
|
||||
clang++ -I. -std=c++20 -Wall -Wformat -o example_single_file example_single_file.cpp
|
||||
|
||||
- name: Build example_null (without c++ runtime)
|
||||
run: |
|
||||
cat > example_single_file.cpp <<'EOF'
|
||||
@@ -563,33 +453,26 @@ jobs:
|
||||
- name: Build example_sdl2_opengl3
|
||||
run: make -C examples/example_sdl2_opengl3
|
||||
|
||||
- name: Build example_sdl3_opengl3
|
||||
run: make -C examples/example_sdl3_opengl3
|
||||
|
||||
- name: Build example_apple_metal
|
||||
run: xcodebuild -project examples/example_apple_metal/example_apple_metal.xcodeproj -target example_apple_metal_macos
|
||||
|
||||
- name: Build example_apple_opengl2
|
||||
run: xcodebuild -project examples/example_apple_opengl2/example_apple_opengl2.xcodeproj -target example_osx_opengl2
|
||||
|
||||
Build-iOS:
|
||||
runs-on: macos-14
|
||||
name: Build - iOS
|
||||
|
||||
iOS:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Build example_apple_metal
|
||||
run: |
|
||||
# Code signing is required, but we disable it because it is irrelevant for CI builds.
|
||||
xcodebuild -project examples/example_apple_metal/example_apple_metal.xcodeproj -target example_apple_metal_ios CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
|
||||
|
||||
Build-Emscripten:
|
||||
runs-on: ubuntu-latest
|
||||
name: Build - Emscripten
|
||||
|
||||
Emscripten:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
@@ -598,7 +481,6 @@ jobs:
|
||||
emsdk-master/emsdk update
|
||||
emsdk-master/emsdk install latest
|
||||
emsdk-master/emsdk activate latest
|
||||
sudo apt-get install build-essential
|
||||
|
||||
- name: Build example_sdl2_opengl3 with Emscripten
|
||||
run: |
|
||||
@@ -607,168 +489,19 @@ jobs:
|
||||
popd
|
||||
make -C examples/example_sdl2_opengl3 -f Makefile.emscripten
|
||||
|
||||
# This build compiles example_glfw_wgpu using Makefile.emscripten and Emscripten GLFW built-in implementation (-sUSE_GLFW=3)
|
||||
# This ensures 2 things: the make build works, and the GLFW built-in implementation is tested
|
||||
- name: Build example_glfw_wgpu with Emscripten/Makefile
|
||||
- name: Build example_glfw_wgpu
|
||||
run: |
|
||||
pushd emsdk-master
|
||||
source ./emsdk_env.sh
|
||||
popd
|
||||
make -C examples/example_glfw_wgpu -f Makefile.emscripten
|
||||
|
||||
# This build compiles example_glfw_wgpu using CMakeLists.txt and Emscripten GLFW contrib port (--use-port=contrib.glfw3)
|
||||
# This ensures 2 things: the CMake build works, and the GLFW contrib port is tested
|
||||
- name: Build example_glfw_wgpu with Emscripten/CMake
|
||||
run: |
|
||||
pushd emsdk-master
|
||||
source ./emsdk_env.sh
|
||||
popd
|
||||
emcc -v
|
||||
emcmake cmake -B build -DCMAKE_BUILD_TYPE=Release examples/example_glfw_wgpu
|
||||
cmake --build build
|
||||
|
||||
Build-Android:
|
||||
runs-on: ubuntu-latest
|
||||
name: Build - Android
|
||||
|
||||
Android:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Build example_android_opengl3
|
||||
run: |
|
||||
cd examples/example_android_opengl3/android
|
||||
gradle assembleDebug --stacktrace
|
||||
|
||||
Test-Windows:
|
||||
runs-on: windows-2025
|
||||
name: Test - Windows
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ github.workspace }}/imgui
|
||||
|
||||
env:
|
||||
MSBUILD_PATH: C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
path: ${{ github.workspace }}/imgui
|
||||
|
||||
- uses: actions/checkout@v5
|
||||
continue-on-error: true
|
||||
with:
|
||||
fetch-depth: 1
|
||||
repository: ocornut/imgui_test_engine
|
||||
path: ${{ github.workspace }}/imgui_test_engine
|
||||
submodules: true
|
||||
|
||||
- name: Fix Tests Projects
|
||||
shell: powershell
|
||||
working-directory: ${{ github.workspace }}/imgui_test_engine
|
||||
run: |
|
||||
# WARNING: This will need updating if toolset/sdk change in project files!
|
||||
gci -recurse -filter "*.vcxproj" | ForEach-Object {
|
||||
# Fix SDK and toolset for most samples.
|
||||
(Get-Content $_.FullName) -Replace "<PlatformToolset>v110</PlatformToolset>","<PlatformToolset>v142</PlatformToolset>" | Set-Content -Path $_.FullName
|
||||
(Get-Content $_.FullName) -Replace "<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>","<WindowsTargetPlatformVersion>10.0.20348.0</WindowsTargetPlatformVersion>" | Set-Content -Path $_.FullName
|
||||
# Fix SDK and toolset for samples that require newer SDK/toolset. At the moment it is only dx12.
|
||||
(Get-Content $_.FullName) -Replace "<PlatformToolset>v140</PlatformToolset>","<PlatformToolset>v142</PlatformToolset>" | Set-Content -Path $_.FullName
|
||||
(Get-Content $_.FullName) -Replace "<WindowsTargetPlatformVersion>10.0.14393.0</WindowsTargetPlatformVersion>","<WindowsTargetPlatformVersion>10.0.20348.0</WindowsTargetPlatformVersion>" | Set-Content -Path $_.FullName
|
||||
}
|
||||
|
||||
- name: Build Tests
|
||||
shell: cmd
|
||||
working-directory: ${{ github.workspace }}/imgui_test_engine/imgui_test_suite
|
||||
run: '"%MSBUILD_PATH%\MSBuild.exe" imgui_test_suite.vcxproj /p:Platform=x64 /p:Configuration=Release /p:ClFlags=/WX -maxcpucount:%NUMBER_OF_PROCESSORS%'
|
||||
|
||||
- name: Run Tests
|
||||
working-directory: ${{ github.workspace }}/imgui_test_engine/imgui_test_suite
|
||||
run: Release/imgui_test_suite.exe -nogui -nopause -v2 -ve4 tests
|
||||
|
||||
- name: Check for Docking
|
||||
id: check_docking
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}/imgui
|
||||
run: echo "has_dock=$(grep -q "#define IMGUI_HAS_DOCK" imgui.h && echo true || echo false)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Run Viewport Tests
|
||||
if: steps.check_docking.outputs.has_dock == 'true'
|
||||
working-directory: ${{ github.workspace }}/imgui_test_engine/imgui_test_suite
|
||||
run: Release/imgui_test_suite.exe -nogui -nopause -v2 -ve4 -viewport-mock viewport
|
||||
|
||||
Test-Linux:
|
||||
runs-on: ubuntu-latest
|
||||
name: Test - Linux
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ github.workspace }}/imgui
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
path: ${{ github.workspace }}/imgui
|
||||
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
repository: ocornut/imgui_test_engine
|
||||
path: ${{ github.workspace }}/imgui_test_engine
|
||||
submodules: true
|
||||
|
||||
- name: Build Tests
|
||||
working-directory: ${{ github.workspace }}/imgui_test_engine/imgui_test_suite
|
||||
run: make -j$(nproc)
|
||||
|
||||
- name: Run Tests
|
||||
working-directory: ${{ github.workspace }}/imgui_test_engine/imgui_test_suite
|
||||
run: ./imgui_test_suite -nogui -nopause -v2 -ve4 tests
|
||||
|
||||
- name: Check for Docking
|
||||
id: check_docking
|
||||
working-directory: ${{ github.workspace }}/imgui
|
||||
run: echo "has_dock=$(grep -q "#define IMGUI_HAS_DOCK" imgui.h && echo true || echo false)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Run Viewport Tests
|
||||
if: steps.check_docking.outputs.has_dock == 'true'
|
||||
working-directory: ${{ github.workspace }}/imgui_test_engine/imgui_test_suite
|
||||
run: ./imgui_test_suite -nogui -nopause -v2 -ve4 -viewport-mock viewport
|
||||
|
||||
Test-MacOS:
|
||||
runs-on: macos-latest
|
||||
name: Test - MacOS
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ github.workspace }}/imgui
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
path: ${{ github.workspace }}/imgui
|
||||
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
repository: ocornut/imgui_test_engine
|
||||
path: ${{ github.workspace }}/imgui_test_engine
|
||||
submodules: true
|
||||
|
||||
- name: Build Tests
|
||||
working-directory: ${{ github.workspace }}/imgui_test_engine/imgui_test_suite
|
||||
run: make -j$(nproc)
|
||||
|
||||
- name: Run Tests
|
||||
working-directory: ${{ github.workspace }}/imgui_test_engine/imgui_test_suite
|
||||
run: ./imgui_test_suite -nogui -nopause -v2 -ve4 tests
|
||||
|
||||
- name: Check for Docking
|
||||
id: check_docking
|
||||
working-directory: ${{ github.workspace }}/imgui
|
||||
run: echo "has_dock=$(grep -q "#define IMGUI_HAS_DOCK" imgui.h && echo true || echo false)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Run Viewport Tests
|
||||
if: steps.check_docking.outputs.has_dock == 'true'
|
||||
working-directory: ${{ github.workspace }}/imgui_test_engine/imgui_test_suite
|
||||
run: ./imgui_test_suite -nogui -nopause -v2 -ve4 -viewport-mock viewport
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
# This is current meant to be run manually, occasionally:
|
||||
# - Run then zip the contents of vulkanArtifact/ into e.g. vulkan_windows_libs_1.4.326
|
||||
# - Upload as an attachment to https://github.com/ocornut/imgui/pull/8925 then change filename in build.yml
|
||||
# - There is a build_windows_vulkan_libs.yml in commit history that we removed thinking this is run so rarely we don't need to pollute CI UI with it.
|
||||
|
||||
# Set default vulkan version if none provided
|
||||
if (-not $env:VULKAN_TAG) { $env:VULKAN_TAG = "1.4.326" }
|
||||
|
||||
# Create output folder
|
||||
mkdir vulkanArtifact
|
||||
|
||||
# Download Vulkan Headers
|
||||
|
||||
Invoke-WebRequest -Uri "https://github.com/KhronosGroup/Vulkan-Headers/archive/refs/tags/v$($env:VULKAN_TAG).zip" -OutFile Vulkan-Headers-$($env:VULKAN_TAG).zip
|
||||
Expand-Archive -Path Vulkan-Headers-$($env:VULKAN_TAG).zip
|
||||
|
||||
# Copy Vulkan Headers to artifact folder
|
||||
|
||||
cp -R Vulkan-Headers-$($env:VULKAN_TAG)\Vulkan-Headers-$($env:VULKAN_TAG)\include vulkanArtifact\Include
|
||||
|
||||
# Download Vulkan Loader
|
||||
|
||||
Invoke-WebRequest -Uri "https://github.com/KhronosGroup/Vulkan-Loader/archive/refs/tags/v$($env:VULKAN_TAG).zip" -OutFile Vulkan-Loader-$($env:VULKAN_TAG).zip
|
||||
Expand-Archive -Path Vulkan-Loader-$($env:VULKAN_TAG).zip
|
||||
|
||||
# Build Vulkan Loader x64
|
||||
|
||||
cmake -S Vulkan-Loader-$($env:VULKAN_TAG)\Vulkan-Loader-$($env:VULKAN_TAG) -B VulkanLoader-build64 -D UPDATE_DEPS=On -A x64
|
||||
cmake --build VulkanLoader-build64
|
||||
mkdir vulkanArtifact\Lib
|
||||
copy VulkanLoader-build64\loader\Debug\vulkan-1.lib vulkanArtifact\Lib
|
||||
|
||||
# Build Vulkan Loader win32
|
||||
|
||||
cmake -S Vulkan-Loader-$($env:VULKAN_TAG)\Vulkan-Loader-$($env:VULKAN_TAG) -B VulkanLoader-build32 -D UPDATE_DEPS=On -A Win32
|
||||
cmake --build VulkanLoader-build32
|
||||
mkdir vulkanArtifact\Lib32
|
||||
copy VulkanLoader-build32\loader\Debug\vulkan-1.lib vulkanArtifact\Lib32
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
#
|
||||
# This is a dummy workflow used to trigger full builds manually.
|
||||
#
|
||||
name: manual
|
||||
|
||||
on: workflow_dispatch
|
||||
|
||||
jobs:
|
||||
manual:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: exit 0
|
||||
+1
-1
@@ -10,7 +10,7 @@ on:
|
||||
|
||||
jobs:
|
||||
PVS-Studio:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
|
||||
@@ -12,7 +12,6 @@ imgui*.ini
|
||||
examples/*/Debug/*
|
||||
examples/*/Release/*
|
||||
examples/*/x64/*
|
||||
examples/*.tmp
|
||||
|
||||
## Visual Studio artifacts
|
||||
.vs
|
||||
@@ -30,14 +29,12 @@ ipch
|
||||
## Getting files created in JSON/Schemas/Catalog/ from a VS2022 update
|
||||
JSON/
|
||||
|
||||
## Commonly used CMake directories & CMake CPM cache
|
||||
## Commonly used CMake directories
|
||||
build*/
|
||||
.cache
|
||||
|
||||
## Xcode & macOS artifacts
|
||||
## Xcode artifacts
|
||||
project.xcworkspace
|
||||
xcuserdata
|
||||
examples/*/*.dSYM
|
||||
|
||||
## Emscripten artifacts
|
||||
examples/*.o.tmp
|
||||
@@ -47,33 +44,18 @@ examples/example_glfw_opengl3/web/*
|
||||
examples/example_glfw_wgpu/web/*
|
||||
examples/example_glfw_wgpu/external/*
|
||||
examples/example_sdl2_opengl3/web/*
|
||||
examples/example_sdl2_wgpu/web/*
|
||||
examples/example_sdl3_opengl3/web/*
|
||||
examples/example_sdl3_wgpu/web/*
|
||||
|
||||
## JetBrains IDE artifacts
|
||||
.idea
|
||||
cmake-build-*
|
||||
|
||||
## VS code artifacts
|
||||
.vscode
|
||||
|
||||
## Unix executables from our example Makefiles
|
||||
examples/example_apple_metal/example_apple_metal
|
||||
examples/example_apple_opengl2/example_apple_opengl2
|
||||
examples/example_glfw_metal/example_glfw_metal
|
||||
examples/example_glfw_opengl2/example_glfw_opengl2
|
||||
examples/example_glfw_opengl3/example_glfw_opengl3
|
||||
examples/example_glfw_vulkan/example_glfw_vulkan
|
||||
examples/example_glut_opengl2/example_glut_opengl2
|
||||
examples/example_null/example_null
|
||||
examples/example_sdl2_metal/example_sdl2_metal
|
||||
examples/example_sdl2_opengl2/example_sdl2_opengl2
|
||||
examples/example_sdl2_opengl3/example_sdl2_opengl3
|
||||
examples/example_sdl2_sdlrenderer2/example_sdl2_sdlrenderer2
|
||||
examples/example_sdl2_vulkan/example_sdl2_vulkan
|
||||
examples/example_sdl3_metal/example_sdl3_metal
|
||||
examples/example_sdl3_opengl3/example_sdl3_opengl3
|
||||
examples/example_sdl3_sdlgpu3/example_sdl3_sdlgpu3
|
||||
examples/example_sdl3_sdlrenderer3/example_sdl3_sdlrenderer3
|
||||
examples/example_sdl3_vulkan/example_sdl3_vulkan
|
||||
examples/example_sdl2_sdlrenderer/example_sdl2_sdlrenderer
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2025 Omar Cornut
|
||||
Copyright (c) 2014-2024 Omar Cornut
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+89
-172
@@ -2,13 +2,12 @@
|
||||
// (Info: Allegro 5 is a cross-platform general purpose library for handling windows, inputs, graphics, etc.)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'ALLEGRO_BITMAP*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
|
||||
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
|
||||
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy ALLEGRO_KEY_* values are obsolete since 1.87 and not supported since 1.91.5]
|
||||
// [X] Platform: Clipboard support (from Allegro 5.1.12).
|
||||
// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||
// Missing features or Issues:
|
||||
// [ ] Renderer: The renderer is suboptimal as we need to unindex our buffers and convert vertices manually.
|
||||
// [X] Renderer: User texture binding. Use 'ALLEGRO_BITMAP*' as ImTextureID. Read the FAQ about ImTextureID!
|
||||
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy ALLEGRO_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
|
||||
// [X] Platform: Clipboard support (from Allegro 5.1.12)
|
||||
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||
// Issues:
|
||||
// [ ] Renderer: The renderer is suboptimal as we need to convert vertices manually.
|
||||
// [ ] Platform: Missing gamepad support.
|
||||
|
||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
@@ -21,16 +20,6 @@
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2025-09-18: Call platform_io.ClearRendererHandlers() and platform_io.ClearPlatformHandlers() on shutdown.
|
||||
// 2025-08-12: Inputs: fixed missing support for ImGuiKey_PrintScreen under Windows, as raw Allegro 5 does not receive it.
|
||||
// 2025-08-12: Added ImGui_ImplAllegro5_SetDisplay() function to change current ALLEGRO_DISPLAY, as Allegro applications often need to do that.
|
||||
// 2025-07-07: Fixed texture update broken on some platforms where ALLEGRO_LOCK_WRITEONLY needed all texels to be rewritten.
|
||||
// 2025-06-11: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplSDLGPU3_CreateFontsTexture() and ImGui_ImplSDLGPU3_DestroyFontsTexture().
|
||||
// 2025-02-18: Added ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress mouse cursor support.
|
||||
// 2025-01-06: Avoid calling al_set_mouse_cursor() repeatedly since it appears to leak on on X11 (#8256).
|
||||
// 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO:
|
||||
// - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn
|
||||
// - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn
|
||||
// 2022-11-30: Renderer: Restoring using al_draw_indexed_prim() when Allegro version is >= 5.2.5.
|
||||
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
|
||||
// 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported).
|
||||
@@ -103,7 +92,6 @@ struct ImGui_ImplAllegro5_Data
|
||||
ALLEGRO_MOUSE_CURSOR* MouseCursorInvisible;
|
||||
ALLEGRO_VERTEX_DECL* VertexDecl;
|
||||
char* ClipboardTextData;
|
||||
ImGuiMouseCursor LastCursor;
|
||||
|
||||
ImVector<ImDrawVertAllegro> BufVertices;
|
||||
ImVector<int> BufIndices;
|
||||
@@ -143,13 +131,6 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data)
|
||||
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
||||
return;
|
||||
|
||||
// Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do.
|
||||
// (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates).
|
||||
if (draw_data->Textures != nullptr)
|
||||
for (ImTextureData* tex : *draw_data->Textures)
|
||||
if (tex->Status != ImTextureStatus_OK)
|
||||
ImGui_ImplAllegro5_UpdateTexture(tex);
|
||||
|
||||
// Backup Allegro state that will be modified
|
||||
ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
|
||||
ALLEGRO_TRANSFORM last_transform = *al_get_current_transform();
|
||||
@@ -163,14 +144,16 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data)
|
||||
ImGui_ImplAllegro5_SetupRenderState(draw_data);
|
||||
|
||||
// Render command lists
|
||||
for (const ImDrawList* draw_list : draw_data->CmdLists)
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
|
||||
ImVector<ImDrawVertAllegro>& vertices = bd->BufVertices;
|
||||
#if ALLEGRO_HAS_DRAW_INDEXED_PRIM
|
||||
vertices.resize(draw_list->VtxBuffer.Size);
|
||||
for (int i = 0; i < draw_list->VtxBuffer.Size; i++)
|
||||
vertices.resize(cmd_list->VtxBuffer.Size);
|
||||
for (int i = 0; i < cmd_list->VtxBuffer.Size; i++)
|
||||
{
|
||||
const ImDrawVert* src_v = &draw_list->VtxBuffer[i];
|
||||
const ImDrawVert* src_v = &cmd_list->VtxBuffer[i];
|
||||
ImDrawVertAllegro* dst_v = &vertices[i];
|
||||
DRAW_VERT_IMGUI_TO_ALLEGRO(dst_v, src_v);
|
||||
}
|
||||
@@ -180,21 +163,21 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data)
|
||||
// FIXME-OPT: Allegro doesn't support 16-bit indices.
|
||||
// You can '#define ImDrawIdx int' in imconfig.h to request Dear ImGui to output 32-bit indices.
|
||||
// Otherwise, we convert them from 16-bit to 32-bit at runtime here, which works perfectly but is a little wasteful.
|
||||
bd->BufIndices.resize(draw_list->IdxBuffer.Size);
|
||||
for (int i = 0; i < draw_list->IdxBuffer.Size; ++i)
|
||||
bd->BufIndices[i] = (int)draw_list->IdxBuffer.Data[i];
|
||||
bd->BufIndices.resize(cmd_list->IdxBuffer.Size);
|
||||
for (int i = 0; i < cmd_list->IdxBuffer.Size; ++i)
|
||||
bd->BufIndices[i] = (int)cmd_list->IdxBuffer.Data[i];
|
||||
indices = bd->BufIndices.Data;
|
||||
}
|
||||
else if (sizeof(ImDrawIdx) == 4)
|
||||
{
|
||||
indices = (const int*)draw_list->IdxBuffer.Data;
|
||||
indices = (const int*)cmd_list->IdxBuffer.Data;
|
||||
}
|
||||
#else
|
||||
// Allegro's implementation of al_draw_indexed_prim() for DX9 was broken until 5.2.5. Unindex buffers ourselves while converting vertex format.
|
||||
vertices.resize(draw_list->IdxBuffer.Size);
|
||||
for (int i = 0; i < draw_list->IdxBuffer.Size; i++)
|
||||
vertices.resize(cmd_list->IdxBuffer.Size);
|
||||
for (int i = 0; i < cmd_list->IdxBuffer.Size; i++)
|
||||
{
|
||||
const ImDrawVert* src_v = &draw_list->VtxBuffer[draw_list->IdxBuffer[i]];
|
||||
const ImDrawVert* src_v = &cmd_list->VtxBuffer[cmd_list->IdxBuffer[i]];
|
||||
ImDrawVertAllegro* dst_v = &vertices[i];
|
||||
DRAW_VERT_IMGUI_TO_ALLEGRO(dst_v, src_v);
|
||||
}
|
||||
@@ -202,9 +185,9 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data)
|
||||
|
||||
// Render command lists
|
||||
ImVec2 clip_off = draw_data->DisplayPos;
|
||||
for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
|
||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
|
||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||
if (pcmd->UserCallback)
|
||||
{
|
||||
// User callback, registered via ImDrawList::AddCallback()
|
||||
@@ -212,7 +195,7 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data)
|
||||
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
||||
ImGui_ImplAllegro5_SetupRenderState(draw_data);
|
||||
else
|
||||
pcmd->UserCallback(draw_list, pcmd);
|
||||
pcmd->UserCallback(cmd_list, pcmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -243,7 +226,43 @@ void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data)
|
||||
|
||||
bool ImGui_ImplAllegro5_CreateDeviceObjects()
|
||||
{
|
||||
// Build texture atlas
|
||||
ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
unsigned char* pixels;
|
||||
int width, height;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
||||
|
||||
// Create texture
|
||||
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
|
||||
int flags = al_get_new_bitmap_flags();
|
||||
int fmt = al_get_new_bitmap_format();
|
||||
al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP | ALLEGRO_MIN_LINEAR | ALLEGRO_MAG_LINEAR);
|
||||
al_set_new_bitmap_format(ALLEGRO_PIXEL_FORMAT_ABGR_8888_LE);
|
||||
ALLEGRO_BITMAP* img = al_create_bitmap(width, height);
|
||||
al_set_new_bitmap_flags(flags);
|
||||
al_set_new_bitmap_format(fmt);
|
||||
if (!img)
|
||||
return false;
|
||||
|
||||
ALLEGRO_LOCKED_REGION* locked_img = al_lock_bitmap(img, al_get_bitmap_format(img), ALLEGRO_LOCK_WRITEONLY);
|
||||
if (!locked_img)
|
||||
{
|
||||
al_destroy_bitmap(img);
|
||||
return false;
|
||||
}
|
||||
memcpy(locked_img->data, pixels, sizeof(int) * width * height);
|
||||
al_unlock_bitmap(img);
|
||||
|
||||
// Convert software texture to hardware texture.
|
||||
ALLEGRO_BITMAP* cloned_img = al_clone_bitmap(img);
|
||||
al_destroy_bitmap(img);
|
||||
if (!cloned_img)
|
||||
return false;
|
||||
|
||||
// Store our identifier
|
||||
io.Fonts->SetTexID((ImTextureID)(intptr_t)cloned_img);
|
||||
bd->Texture = cloned_img;
|
||||
|
||||
// Create an invisible mouse cursor
|
||||
// Because al_hide_mouse_cursor() seems to mess up with the actual inputs..
|
||||
@@ -254,82 +273,16 @@ bool ImGui_ImplAllegro5_CreateDeviceObjects()
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGui_ImplAllegro5_UpdateTexture(ImTextureData* tex)
|
||||
{
|
||||
if (tex->Status == ImTextureStatus_WantCreate)
|
||||
{
|
||||
// Create and upload new texture to graphics system
|
||||
//IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height);
|
||||
IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr);
|
||||
IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
|
||||
|
||||
// Create texture
|
||||
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
|
||||
const int new_bitmap_flags = al_get_new_bitmap_flags();
|
||||
int new_bitmap_format = al_get_new_bitmap_format();
|
||||
al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP | ALLEGRO_MIN_LINEAR | ALLEGRO_MAG_LINEAR);
|
||||
al_set_new_bitmap_format(ALLEGRO_PIXEL_FORMAT_ABGR_8888_LE);
|
||||
ALLEGRO_BITMAP* cpu_bitmap = al_create_bitmap(tex->Width, tex->Height);
|
||||
IM_ASSERT(cpu_bitmap != nullptr && "Backend failed to create texture!");
|
||||
|
||||
// Upload pixels
|
||||
ALLEGRO_LOCKED_REGION* locked_region = al_lock_bitmap(cpu_bitmap, al_get_bitmap_format(cpu_bitmap), ALLEGRO_LOCK_WRITEONLY);
|
||||
IM_ASSERT(locked_region != nullptr && "Backend failed to create texture!");
|
||||
memcpy(locked_region->data, tex->GetPixels(), tex->GetSizeInBytes());
|
||||
al_unlock_bitmap(cpu_bitmap);
|
||||
|
||||
// Convert software texture to hardware texture.
|
||||
al_set_new_bitmap_flags(ALLEGRO_VIDEO_BITMAP);
|
||||
al_set_new_bitmap_format(ALLEGRO_PIXEL_FORMAT_ANY_32_WITH_ALPHA);
|
||||
ALLEGRO_BITMAP* gpu_bitmap = al_clone_bitmap(cpu_bitmap);
|
||||
al_destroy_bitmap(cpu_bitmap);
|
||||
IM_ASSERT(gpu_bitmap != nullptr && "Backend failed to create texture!");
|
||||
|
||||
al_set_new_bitmap_flags(new_bitmap_flags);
|
||||
al_set_new_bitmap_format(new_bitmap_format);
|
||||
|
||||
// Store identifiers
|
||||
tex->SetTexID((ImTextureID)(intptr_t)gpu_bitmap);
|
||||
tex->SetStatus(ImTextureStatus_OK);
|
||||
}
|
||||
else if (tex->Status == ImTextureStatus_WantUpdates)
|
||||
{
|
||||
// Update selected blocks. We only ever write to textures regions which have never been used before!
|
||||
// This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region.
|
||||
ImTextureRect r = tex->UpdateRect; // Bounding box encompassing all individual updates
|
||||
ALLEGRO_BITMAP* gpu_bitmap = (ALLEGRO_BITMAP*)(intptr_t)tex->TexID;
|
||||
ALLEGRO_LOCKED_REGION* locked_region = al_lock_bitmap_region(gpu_bitmap, r.x, r.y, r.w, r.h, al_get_bitmap_format(gpu_bitmap), ALLEGRO_LOCK_WRITEONLY);
|
||||
IM_ASSERT(locked_region && "Backend failed to update texture!");
|
||||
for (int y = 0; y < r.h; y++)
|
||||
memcpy((unsigned char*)locked_region->data + locked_region->pitch * y, tex->GetPixelsAt(r.x, r.y + y), r.w * tex->BytesPerPixel); // dst, src, block pitch
|
||||
al_unlock_bitmap(gpu_bitmap);
|
||||
tex->SetStatus(ImTextureStatus_OK);
|
||||
}
|
||||
else if (tex->Status == ImTextureStatus_WantDestroy)
|
||||
{
|
||||
ALLEGRO_BITMAP* backend_tex = (ALLEGRO_BITMAP*)(intptr_t)tex->TexID;
|
||||
if (backend_tex)
|
||||
al_destroy_bitmap(backend_tex);
|
||||
|
||||
// Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running)
|
||||
tex->SetTexID(ImTextureID_Invalid);
|
||||
tex->SetStatus(ImTextureStatus_Destroyed);
|
||||
}
|
||||
}
|
||||
|
||||
void ImGui_ImplAllegro5_InvalidateDeviceObjects()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
|
||||
|
||||
// Destroy all textures
|
||||
for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)
|
||||
if (tex->RefCount == 1)
|
||||
{
|
||||
tex->SetStatus(ImTextureStatus_WantDestroy);
|
||||
ImGui_ImplAllegro5_UpdateTexture(tex);
|
||||
}
|
||||
|
||||
// Destroy mouse cursor
|
||||
if (bd->Texture)
|
||||
{
|
||||
io.Fonts->SetTexID(0);
|
||||
al_destroy_bitmap(bd->Texture);
|
||||
bd->Texture = nullptr;
|
||||
}
|
||||
if (bd->MouseCursorInvisible)
|
||||
{
|
||||
al_destroy_mouse_cursor(bd->MouseCursorInvisible);
|
||||
@@ -338,7 +291,7 @@ void ImGui_ImplAllegro5_InvalidateDeviceObjects()
|
||||
}
|
||||
|
||||
#if ALLEGRO_HAS_CLIPBOARD
|
||||
static const char* ImGui_ImplAllegro5_GetClipboardText(ImGuiContext*)
|
||||
static const char* ImGui_ImplAllegro5_GetClipboardText(void*)
|
||||
{
|
||||
ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
|
||||
if (bd->ClipboardTextData)
|
||||
@@ -347,16 +300,14 @@ static const char* ImGui_ImplAllegro5_GetClipboardText(ImGuiContext*)
|
||||
return bd->ClipboardTextData;
|
||||
}
|
||||
|
||||
static void ImGui_ImplAllegro5_SetClipboardText(ImGuiContext*, const char* text)
|
||||
static void ImGui_ImplAllegro5_SetClipboardText(void*, const char* text)
|
||||
{
|
||||
ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
|
||||
al_set_clipboard_text(bd->Display, text);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Not static to allow third-party code to use that if they want to (but undocumented)
|
||||
ImGuiKey ImGui_ImplAllegro5_KeyCodeToImGuiKey(int key_code);
|
||||
ImGuiKey ImGui_ImplAllegro5_KeyCodeToImGuiKey(int key_code)
|
||||
static ImGuiKey ImGui_ImplAllegro5_KeyCodeToImGuiKey(int key_code)
|
||||
{
|
||||
switch (key_code)
|
||||
{
|
||||
@@ -480,16 +431,25 @@ bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display)
|
||||
io.BackendPlatformUserData = (void*)bd;
|
||||
io.BackendPlatformName = io.BackendRendererName = "imgui_impl_allegro5";
|
||||
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render.
|
||||
|
||||
bd->LastCursor = ALLEGRO_SYSTEM_MOUSE_CURSOR_NONE;
|
||||
bd->Display = display;
|
||||
|
||||
ImGui_ImplAllegro5_SetDisplay(display);
|
||||
// Create custom vertex declaration.
|
||||
// Unfortunately Allegro doesn't support 32-bit packed colors so we have to convert them to 4 floats.
|
||||
// We still use a custom declaration to use 'ALLEGRO_PRIM_TEX_COORD' instead of 'ALLEGRO_PRIM_TEX_COORD_PIXEL' else we can't do a reliable conversion.
|
||||
ALLEGRO_VERTEX_ELEMENT elems[] =
|
||||
{
|
||||
{ ALLEGRO_PRIM_POSITION, ALLEGRO_PRIM_FLOAT_2, offsetof(ImDrawVertAllegro, pos) },
|
||||
{ ALLEGRO_PRIM_TEX_COORD, ALLEGRO_PRIM_FLOAT_2, offsetof(ImDrawVertAllegro, uv) },
|
||||
{ ALLEGRO_PRIM_COLOR_ATTR, 0, offsetof(ImDrawVertAllegro, col) },
|
||||
{ 0, 0, 0 }
|
||||
};
|
||||
bd->VertexDecl = al_create_vertex_decl(elems, sizeof(ImDrawVertAllegro));
|
||||
|
||||
#if ALLEGRO_HAS_CLIPBOARD
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
platform_io.Platform_SetClipboardTextFn = ImGui_ImplAllegro5_SetClipboardText;
|
||||
platform_io.Platform_GetClipboardTextFn = ImGui_ImplAllegro5_GetClipboardText;
|
||||
io.SetClipboardTextFn = ImGui_ImplAllegro5_SetClipboardText;
|
||||
io.GetClipboardTextFn = ImGui_ImplAllegro5_GetClipboardText;
|
||||
io.ClipboardUserData = nullptr;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
@@ -500,7 +460,6 @@ void ImGui_ImplAllegro5_Shutdown()
|
||||
ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
|
||||
ImGui_ImplAllegro5_InvalidateDeviceObjects();
|
||||
if (bd->VertexDecl)
|
||||
@@ -510,39 +469,10 @@ void ImGui_ImplAllegro5_Shutdown()
|
||||
|
||||
io.BackendPlatformName = io.BackendRendererName = nullptr;
|
||||
io.BackendPlatformUserData = nullptr;
|
||||
io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_RendererHasTextures);
|
||||
platform_io.ClearRendererHandlers();
|
||||
platform_io.ClearPlatformHandlers();
|
||||
io.BackendFlags &= ~ImGuiBackendFlags_HasMouseCursors;
|
||||
IM_DELETE(bd);
|
||||
}
|
||||
|
||||
void ImGui_ImplAllegro5_SetDisplay(ALLEGRO_DISPLAY* display)
|
||||
{
|
||||
ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
|
||||
bd->Display = display;
|
||||
|
||||
if (bd->VertexDecl)
|
||||
{
|
||||
al_destroy_vertex_decl(bd->VertexDecl);
|
||||
bd->VertexDecl = NULL;
|
||||
}
|
||||
|
||||
if (bd->Display && !bd->VertexDecl)
|
||||
{
|
||||
// Create custom vertex declaration.
|
||||
// Unfortunately Allegro doesn't support 32-bits packed colors so we have to convert them to 4 floats.
|
||||
// We still use a custom declaration to use 'ALLEGRO_PRIM_TEX_COORD' instead of 'ALLEGRO_PRIM_TEX_COORD_PIXEL' else we can't do a reliable conversion.
|
||||
ALLEGRO_VERTEX_ELEMENT elems[] =
|
||||
{
|
||||
{ ALLEGRO_PRIM_POSITION, ALLEGRO_PRIM_FLOAT_2, offsetof(ImDrawVertAllegro, pos) },
|
||||
{ ALLEGRO_PRIM_TEX_COORD, ALLEGRO_PRIM_FLOAT_2, offsetof(ImDrawVertAllegro, uv) },
|
||||
{ ALLEGRO_PRIM_COLOR_ATTR, 0, offsetof(ImDrawVertAllegro, col) },
|
||||
{ 0, 0, 0 }
|
||||
};
|
||||
bd->VertexDecl = al_create_vertex_decl(elems, sizeof(ImDrawVertAllegro));
|
||||
}
|
||||
}
|
||||
|
||||
// ev->keyboard.modifiers seems always zero so using that...
|
||||
static void ImGui_ImplAllegro5_UpdateKeyModifiers()
|
||||
{
|
||||
@@ -633,16 +563,9 @@ static void ImGui_ImplAllegro5_UpdateMouseCursor()
|
||||
|
||||
ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
|
||||
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
|
||||
|
||||
// Hide OS mouse cursor if imgui is drawing it
|
||||
if (io.MouseDrawCursor)
|
||||
imgui_cursor = ImGuiMouseCursor_None;
|
||||
|
||||
if (bd->LastCursor == imgui_cursor)
|
||||
return;
|
||||
bd->LastCursor = imgui_cursor;
|
||||
if (imgui_cursor == ImGuiMouseCursor_None)
|
||||
if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None)
|
||||
{
|
||||
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
|
||||
al_set_mouse_cursor(bd->Display, bd->MouseCursorInvisible);
|
||||
}
|
||||
else
|
||||
@@ -656,8 +579,6 @@ static void ImGui_ImplAllegro5_UpdateMouseCursor()
|
||||
case ImGuiMouseCursor_ResizeEW: cursor_id = ALLEGRO_SYSTEM_MOUSE_CURSOR_RESIZE_E; break;
|
||||
case ImGuiMouseCursor_ResizeNESW: cursor_id = ALLEGRO_SYSTEM_MOUSE_CURSOR_RESIZE_NE; break;
|
||||
case ImGuiMouseCursor_ResizeNWSE: cursor_id = ALLEGRO_SYSTEM_MOUSE_CURSOR_RESIZE_NW; break;
|
||||
case ImGuiMouseCursor_Wait: cursor_id = ALLEGRO_SYSTEM_MOUSE_CURSOR_BUSY; break;
|
||||
case ImGuiMouseCursor_Progress: cursor_id = ALLEGRO_SYSTEM_MOUSE_CURSOR_PROGRESS; break;
|
||||
case ImGuiMouseCursor_NotAllowed: cursor_id = ALLEGRO_SYSTEM_MOUSE_CURSOR_UNAVAILABLE; break;
|
||||
}
|
||||
al_set_system_mouse_cursor(bd->Display, cursor_id);
|
||||
@@ -669,11 +590,12 @@ void ImGui_ImplAllegro5_NewFrame()
|
||||
ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplAllegro5_Init()?");
|
||||
|
||||
if (!bd->MouseCursorInvisible)
|
||||
if (!bd->Texture)
|
||||
ImGui_ImplAllegro5_CreateDeviceObjects();
|
||||
|
||||
// Setup display size (every frame to accommodate for window resizing)
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
// Setup display size (every frame to accommodate for window resizing)
|
||||
int w, h;
|
||||
w = al_get_display_width(bd->Display);
|
||||
h = al_get_display_height(bd->Display);
|
||||
@@ -684,11 +606,6 @@ void ImGui_ImplAllegro5_NewFrame()
|
||||
io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f);
|
||||
bd->Time = current_time;
|
||||
|
||||
// Allegro 5 doesn't receive PrintScreen under Windows
|
||||
#ifdef _WIN32
|
||||
io.AddKeyEvent(ImGuiKey_PrintScreen, (::GetAsyncKeyState(VK_SNAPSHOT) & 0x8000) != 0);
|
||||
#endif
|
||||
|
||||
// Setup mouse cursor shape
|
||||
ImGui_ImplAllegro5_UpdateMouseCursor();
|
||||
}
|
||||
|
||||
+5
-11
@@ -2,12 +2,11 @@
|
||||
// (Info: Allegro 5 is a cross-platform general purpose library for handling windows, inputs, graphics, etc.)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'ALLEGRO_BITMAP*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
|
||||
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
|
||||
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy ALLEGRO_KEY_* values are obsolete since 1.87 and not supported since 1.91.5]
|
||||
// [X] Platform: Clipboard support (from Allegro 5.1.12).
|
||||
// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||
// Missing features or Issues:
|
||||
// [X] Renderer: User texture binding. Use 'ALLEGRO_BITMAP*' as ImTextureID. Read the FAQ about ImTextureID!
|
||||
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy ALLEGRO_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
|
||||
// [X] Platform: Clipboard support (from Allegro 5.1.12)
|
||||
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
|
||||
// Issues:
|
||||
// [ ] Renderer: The renderer is suboptimal as we need to unindex our buffers and convert vertices manually.
|
||||
// [ ] Platform: Missing gamepad support.
|
||||
|
||||
@@ -26,19 +25,14 @@
|
||||
struct ALLEGRO_DISPLAY;
|
||||
union ALLEGRO_EVENT;
|
||||
|
||||
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
|
||||
IMGUI_IMPL_API bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display);
|
||||
IMGUI_IMPL_API void ImGui_ImplAllegro5_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplAllegro5_NewFrame();
|
||||
IMGUI_IMPL_API void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data);
|
||||
IMGUI_IMPL_API bool ImGui_ImplAllegro5_ProcessEvent(ALLEGRO_EVENT* event);
|
||||
IMGUI_IMPL_API void ImGui_ImplAllegro5_SetDisplay(ALLEGRO_DISPLAY* display);
|
||||
|
||||
// Use if you want to reset your rendering device without losing Dear ImGui state.
|
||||
IMGUI_IMPL_API bool ImGui_ImplAllegro5_CreateDeviceObjects();
|
||||
IMGUI_IMPL_API void ImGui_ImplAllegro5_InvalidateDeviceObjects();
|
||||
|
||||
// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = NULL to handle this manually.
|
||||
IMGUI_IMPL_API void ImGui_ImplAllegro5_UpdateTexture(ImTextureData* tex);
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
|
||||
+4
-4
@@ -2,12 +2,12 @@
|
||||
// This needs to be used along with the OpenGL 3 Renderer (imgui_impl_opengl3)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy AKEYCODE_* values are obsolete since 1.87 and not supported since 1.91.5]
|
||||
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy AKEYCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
|
||||
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen.
|
||||
// Missing features or Issues:
|
||||
// Missing features:
|
||||
// [ ] Platform: Clipboard support.
|
||||
// [ ] Platform: Gamepad support.
|
||||
// [ ] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android.
|
||||
// [ ] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||
// [ ] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android.
|
||||
// Important:
|
||||
// - Consider using SDL or GLFW backend on Android, which will be more full-featured than this.
|
||||
// - FIXME: On-screen keyboard currently needs to be enabled by the application (see examples/ and issue #3446)
|
||||
|
||||
+4
-5
@@ -2,12 +2,12 @@
|
||||
// This needs to be used along with the OpenGL 3 Renderer (imgui_impl_opengl3)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy AKEYCODE_* values are obsolete since 1.87 and not supported since 1.91.5]
|
||||
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy AKEYCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
|
||||
// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen.
|
||||
// Missing features or Issues:
|
||||
// Missing features:
|
||||
// [ ] Platform: Clipboard support.
|
||||
// [ ] Platform: Gamepad support.
|
||||
// [ ] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android.
|
||||
// [ ] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
|
||||
// [ ] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android.
|
||||
// Important:
|
||||
// - Consider using SDL or GLFW backend on Android, which will be more full-featured than this.
|
||||
// - FIXME: On-screen keyboard currently needs to be enabled by the application (see examples/ and issue #3446)
|
||||
@@ -28,7 +28,6 @@
|
||||
struct ANativeWindow;
|
||||
struct AInputEvent;
|
||||
|
||||
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
|
||||
IMGUI_IMPL_API bool ImGui_ImplAndroid_Init(ANativeWindow* window);
|
||||
IMGUI_IMPL_API int32_t ImGui_ImplAndroid_HandleInputEvent(const AInputEvent* input_event);
|
||||
IMGUI_IMPL_API void ImGui_ImplAndroid_Shutdown();
|
||||
|
||||
+176
-245
@@ -2,9 +2,8 @@
|
||||
// This needs to be used along with a Platform Backend (e.g. Win32)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
|
||||
// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
|
||||
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
|
||||
// [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID!
|
||||
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
|
||||
|
||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
@@ -16,11 +15,6 @@
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown.
|
||||
// 2025-06-11: DirectX10: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas.
|
||||
// 2025-05-07: DirectX10: Honor draw_data->FramebufferScale to allow for custom backends and experiment using it (consistently with other renderer backends, even though in normal condition it is not set under Windows).
|
||||
// 2025-01-06: DirectX10: Expose selected render state in ImGui_ImplDX10_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
|
||||
// 2024-10-07: DirectX10: Changed default texture sampler to Clamp instead of Repeat/Wrap.
|
||||
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
|
||||
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
|
||||
// 2021-05-19: DirectX10: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
|
||||
@@ -51,19 +45,7 @@
|
||||
#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
|
||||
#endif
|
||||
|
||||
// Clang/GCC warnings with -Weverything
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
|
||||
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||
#endif
|
||||
|
||||
// DirectX10 data
|
||||
struct ImGui_ImplDX10_Texture
|
||||
{
|
||||
ID3D10Texture2D* pTexture;
|
||||
ID3D10ShaderResourceView* pTextureView;
|
||||
};
|
||||
|
||||
// DirectX data
|
||||
struct ImGui_ImplDX10_Data
|
||||
{
|
||||
ID3D10Device* pd3dDevice;
|
||||
@@ -74,7 +56,8 @@ struct ImGui_ImplDX10_Data
|
||||
ID3D10InputLayout* pInputLayout;
|
||||
ID3D10Buffer* pVertexConstantBuffer;
|
||||
ID3D10PixelShader* pPixelShader;
|
||||
ID3D10SamplerState* pTexSamplerLinear;
|
||||
ID3D10SamplerState* pFontSampler;
|
||||
ID3D10ShaderResourceView* pFontTextureView;
|
||||
ID3D10RasterizerState* pRasterizerState;
|
||||
ID3D10BlendState* pBlendState;
|
||||
ID3D10DepthStencilState* pDepthStencilState;
|
||||
@@ -97,24 +80,102 @@ static ImGui_ImplDX10_Data* ImGui_ImplDX10_GetBackendData()
|
||||
}
|
||||
|
||||
// Functions
|
||||
static void ImGui_ImplDX10_SetupRenderState(ImDrawData* draw_data, ID3D10Device* device)
|
||||
static void ImGui_ImplDX10_SetupRenderState(ImDrawData* draw_data, ID3D10Device* ctx)
|
||||
{
|
||||
ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
|
||||
|
||||
// Setup viewport
|
||||
D3D10_VIEWPORT vp = {};
|
||||
vp.Width = (UINT)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
|
||||
vp.Height = (UINT)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
|
||||
D3D10_VIEWPORT vp;
|
||||
memset(&vp, 0, sizeof(D3D10_VIEWPORT));
|
||||
vp.Width = (UINT)draw_data->DisplaySize.x;
|
||||
vp.Height = (UINT)draw_data->DisplaySize.y;
|
||||
vp.MinDepth = 0.0f;
|
||||
vp.MaxDepth = 1.0f;
|
||||
vp.TopLeftX = vp.TopLeftY = 0;
|
||||
device->RSSetViewports(1, &vp);
|
||||
ctx->RSSetViewports(1, &vp);
|
||||
|
||||
// Bind shader and vertex buffers
|
||||
unsigned int stride = sizeof(ImDrawVert);
|
||||
unsigned int offset = 0;
|
||||
ctx->IASetInputLayout(bd->pInputLayout);
|
||||
ctx->IASetVertexBuffers(0, 1, &bd->pVB, &stride, &offset);
|
||||
ctx->IASetIndexBuffer(bd->pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
|
||||
ctx->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
ctx->VSSetShader(bd->pVertexShader);
|
||||
ctx->VSSetConstantBuffers(0, 1, &bd->pVertexConstantBuffer);
|
||||
ctx->PSSetShader(bd->pPixelShader);
|
||||
ctx->PSSetSamplers(0, 1, &bd->pFontSampler);
|
||||
ctx->GSSetShader(nullptr);
|
||||
|
||||
// Setup render state
|
||||
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
|
||||
ctx->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff);
|
||||
ctx->OMSetDepthStencilState(bd->pDepthStencilState, 0);
|
||||
ctx->RSSetState(bd->pRasterizerState);
|
||||
}
|
||||
|
||||
// Render function
|
||||
void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data)
|
||||
{
|
||||
// Avoid rendering when minimized
|
||||
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
||||
return;
|
||||
|
||||
ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
|
||||
ID3D10Device* ctx = bd->pd3dDevice;
|
||||
|
||||
// Create and grow vertex/index buffers if needed
|
||||
if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount)
|
||||
{
|
||||
if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; }
|
||||
bd->VertexBufferSize = draw_data->TotalVtxCount + 5000;
|
||||
D3D10_BUFFER_DESC desc;
|
||||
memset(&desc, 0, sizeof(D3D10_BUFFER_DESC));
|
||||
desc.Usage = D3D10_USAGE_DYNAMIC;
|
||||
desc.ByteWidth = bd->VertexBufferSize * sizeof(ImDrawVert);
|
||||
desc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
|
||||
desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
|
||||
desc.MiscFlags = 0;
|
||||
if (ctx->CreateBuffer(&desc, nullptr, &bd->pVB) < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bd->pIB || bd->IndexBufferSize < draw_data->TotalIdxCount)
|
||||
{
|
||||
if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; }
|
||||
bd->IndexBufferSize = draw_data->TotalIdxCount + 10000;
|
||||
D3D10_BUFFER_DESC desc;
|
||||
memset(&desc, 0, sizeof(D3D10_BUFFER_DESC));
|
||||
desc.Usage = D3D10_USAGE_DYNAMIC;
|
||||
desc.ByteWidth = bd->IndexBufferSize * sizeof(ImDrawIdx);
|
||||
desc.BindFlags = D3D10_BIND_INDEX_BUFFER;
|
||||
desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
|
||||
if (ctx->CreateBuffer(&desc, nullptr, &bd->pIB) < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy and convert all vertices into a single contiguous buffer
|
||||
ImDrawVert* vtx_dst = nullptr;
|
||||
ImDrawIdx* idx_dst = nullptr;
|
||||
bd->pVB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&vtx_dst);
|
||||
bd->pIB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&idx_dst);
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
|
||||
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
||||
vtx_dst += cmd_list->VtxBuffer.Size;
|
||||
idx_dst += cmd_list->IdxBuffer.Size;
|
||||
}
|
||||
bd->pVB->Unmap();
|
||||
bd->pIB->Unmap();
|
||||
|
||||
// Setup orthographic projection matrix into our constant buffer
|
||||
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
|
||||
void* mapped_resource;
|
||||
if (bd->pVertexConstantBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &mapped_resource) == S_OK)
|
||||
{
|
||||
void* mapped_resource;
|
||||
if (bd->pVertexConstantBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK)
|
||||
return;
|
||||
VERTEX_CONSTANT_BUFFER_DX10* constant_buffer = (VERTEX_CONSTANT_BUFFER_DX10*)mapped_resource;
|
||||
float L = draw_data->DisplayPos.x;
|
||||
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
|
||||
@@ -131,86 +192,6 @@ static void ImGui_ImplDX10_SetupRenderState(ImDrawData* draw_data, ID3D10Device*
|
||||
bd->pVertexConstantBuffer->Unmap();
|
||||
}
|
||||
|
||||
// Setup shader and vertex buffers
|
||||
unsigned int stride = sizeof(ImDrawVert);
|
||||
unsigned int offset = 0;
|
||||
device->IASetInputLayout(bd->pInputLayout);
|
||||
device->IASetVertexBuffers(0, 1, &bd->pVB, &stride, &offset);
|
||||
device->IASetIndexBuffer(bd->pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
|
||||
device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
device->VSSetShader(bd->pVertexShader);
|
||||
device->VSSetConstantBuffers(0, 1, &bd->pVertexConstantBuffer);
|
||||
device->PSSetShader(bd->pPixelShader);
|
||||
device->PSSetSamplers(0, 1, &bd->pTexSamplerLinear);
|
||||
device->GSSetShader(nullptr);
|
||||
|
||||
// Setup render state
|
||||
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
|
||||
device->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff);
|
||||
device->OMSetDepthStencilState(bd->pDepthStencilState, 0);
|
||||
device->RSSetState(bd->pRasterizerState);
|
||||
}
|
||||
|
||||
// Render function
|
||||
void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data)
|
||||
{
|
||||
// Avoid rendering when minimized
|
||||
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
||||
return;
|
||||
|
||||
ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
|
||||
ID3D10Device* device = bd->pd3dDevice;
|
||||
|
||||
// Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do.
|
||||
// (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates).
|
||||
if (draw_data->Textures != nullptr)
|
||||
for (ImTextureData* tex : *draw_data->Textures)
|
||||
if (tex->Status != ImTextureStatus_OK)
|
||||
ImGui_ImplDX10_UpdateTexture(tex);
|
||||
|
||||
// Create and grow vertex/index buffers if needed
|
||||
if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount)
|
||||
{
|
||||
if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; }
|
||||
bd->VertexBufferSize = draw_data->TotalVtxCount + 5000;
|
||||
D3D10_BUFFER_DESC desc = {};
|
||||
desc.Usage = D3D10_USAGE_DYNAMIC;
|
||||
desc.ByteWidth = bd->VertexBufferSize * sizeof(ImDrawVert);
|
||||
desc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
|
||||
desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
|
||||
desc.MiscFlags = 0;
|
||||
if (device->CreateBuffer(&desc, nullptr, &bd->pVB) < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bd->pIB || bd->IndexBufferSize < draw_data->TotalIdxCount)
|
||||
{
|
||||
if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; }
|
||||
bd->IndexBufferSize = draw_data->TotalIdxCount + 10000;
|
||||
D3D10_BUFFER_DESC desc = {};
|
||||
desc.Usage = D3D10_USAGE_DYNAMIC;
|
||||
desc.ByteWidth = bd->IndexBufferSize * sizeof(ImDrawIdx);
|
||||
desc.BindFlags = D3D10_BIND_INDEX_BUFFER;
|
||||
desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
|
||||
if (device->CreateBuffer(&desc, nullptr, &bd->pIB) < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy and convert all vertices into a single contiguous buffer
|
||||
ImDrawVert* vtx_dst = nullptr;
|
||||
ImDrawIdx* idx_dst = nullptr;
|
||||
bd->pVB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&vtx_dst);
|
||||
bd->pIB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&idx_dst);
|
||||
for (const ImDrawList* draw_list : draw_data->CmdLists)
|
||||
{
|
||||
memcpy(vtx_dst, draw_list->VtxBuffer.Data, draw_list->VtxBuffer.Size * sizeof(ImDrawVert));
|
||||
memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
||||
vtx_dst += draw_list->VtxBuffer.Size;
|
||||
idx_dst += draw_list->IdxBuffer.Size;
|
||||
}
|
||||
bd->pVB->Unmap();
|
||||
bd->pIB->Unmap();
|
||||
|
||||
// Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!)
|
||||
struct BACKUP_DX10_STATE
|
||||
{
|
||||
@@ -236,126 +217,100 @@ void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data)
|
||||
};
|
||||
BACKUP_DX10_STATE old = {};
|
||||
old.ScissorRectsCount = old.ViewportsCount = D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE;
|
||||
device->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects);
|
||||
device->RSGetViewports(&old.ViewportsCount, old.Viewports);
|
||||
device->RSGetState(&old.RS);
|
||||
device->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask);
|
||||
device->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef);
|
||||
device->PSGetShaderResources(0, 1, &old.PSShaderResource);
|
||||
device->PSGetSamplers(0, 1, &old.PSSampler);
|
||||
device->PSGetShader(&old.PS);
|
||||
device->VSGetShader(&old.VS);
|
||||
device->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer);
|
||||
device->GSGetShader(&old.GS);
|
||||
device->IAGetPrimitiveTopology(&old.PrimitiveTopology);
|
||||
device->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset);
|
||||
device->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset);
|
||||
device->IAGetInputLayout(&old.InputLayout);
|
||||
ctx->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects);
|
||||
ctx->RSGetViewports(&old.ViewportsCount, old.Viewports);
|
||||
ctx->RSGetState(&old.RS);
|
||||
ctx->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask);
|
||||
ctx->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef);
|
||||
ctx->PSGetShaderResources(0, 1, &old.PSShaderResource);
|
||||
ctx->PSGetSamplers(0, 1, &old.PSSampler);
|
||||
ctx->PSGetShader(&old.PS);
|
||||
ctx->VSGetShader(&old.VS);
|
||||
ctx->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer);
|
||||
ctx->GSGetShader(&old.GS);
|
||||
ctx->IAGetPrimitiveTopology(&old.PrimitiveTopology);
|
||||
ctx->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset);
|
||||
ctx->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset);
|
||||
ctx->IAGetInputLayout(&old.InputLayout);
|
||||
|
||||
// Setup desired DX state
|
||||
ImGui_ImplDX10_SetupRenderState(draw_data, device);
|
||||
// Setup render state structure (for callbacks and custom texture bindings)
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
ImGui_ImplDX10_RenderState render_state;
|
||||
render_state.Device = bd->pd3dDevice;
|
||||
render_state.SamplerDefault = bd->pTexSamplerLinear;
|
||||
render_state.VertexConstantBuffer = bd->pVertexConstantBuffer;
|
||||
platform_io.Renderer_RenderState = &render_state;
|
||||
ImGui_ImplDX10_SetupRenderState(draw_data, ctx);
|
||||
|
||||
// Render command lists
|
||||
// (Because we merged all buffers into a single one, we maintain our own offset into them)
|
||||
int global_vtx_offset = 0;
|
||||
int global_idx_offset = 0;
|
||||
ImVec2 clip_off = draw_data->DisplayPos;
|
||||
ImVec2 clip_scale = draw_data->FramebufferScale;
|
||||
for (const ImDrawList* draw_list : draw_data->CmdLists)
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
|
||||
if (pcmd->UserCallback != nullptr)
|
||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||
if (pcmd->UserCallback)
|
||||
{
|
||||
// User callback, registered via ImDrawList::AddCallback()
|
||||
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
|
||||
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
||||
ImGui_ImplDX10_SetupRenderState(draw_data, device);
|
||||
ImGui_ImplDX10_SetupRenderState(draw_data, ctx);
|
||||
else
|
||||
pcmd->UserCallback(draw_list, pcmd);
|
||||
pcmd->UserCallback(cmd_list, pcmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Project scissor/clipping rectangles into framebuffer space
|
||||
ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
|
||||
ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
|
||||
ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y);
|
||||
ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y);
|
||||
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
|
||||
continue;
|
||||
|
||||
// Apply scissor/clipping rectangle
|
||||
const D3D10_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y };
|
||||
device->RSSetScissorRects(1, &r);
|
||||
ctx->RSSetScissorRects(1, &r);
|
||||
|
||||
// Bind texture, Draw
|
||||
ID3D10ShaderResourceView* texture_srv = (ID3D10ShaderResourceView*)pcmd->GetTexID();
|
||||
device->PSSetShaderResources(0, 1, &texture_srv);
|
||||
device->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset);
|
||||
ctx->PSSetShaderResources(0, 1, &texture_srv);
|
||||
ctx->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset);
|
||||
}
|
||||
}
|
||||
global_idx_offset += draw_list->IdxBuffer.Size;
|
||||
global_vtx_offset += draw_list->VtxBuffer.Size;
|
||||
global_idx_offset += cmd_list->IdxBuffer.Size;
|
||||
global_vtx_offset += cmd_list->VtxBuffer.Size;
|
||||
}
|
||||
platform_io.Renderer_RenderState = nullptr;
|
||||
|
||||
// Restore modified DX state
|
||||
device->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects);
|
||||
device->RSSetViewports(old.ViewportsCount, old.Viewports);
|
||||
device->RSSetState(old.RS); if (old.RS) old.RS->Release();
|
||||
device->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release();
|
||||
device->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release();
|
||||
device->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release();
|
||||
device->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release();
|
||||
device->PSSetShader(old.PS); if (old.PS) old.PS->Release();
|
||||
device->VSSetShader(old.VS); if (old.VS) old.VS->Release();
|
||||
device->GSSetShader(old.GS); if (old.GS) old.GS->Release();
|
||||
device->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release();
|
||||
device->IASetPrimitiveTopology(old.PrimitiveTopology);
|
||||
device->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release();
|
||||
device->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release();
|
||||
device->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release();
|
||||
ctx->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects);
|
||||
ctx->RSSetViewports(old.ViewportsCount, old.Viewports);
|
||||
ctx->RSSetState(old.RS); if (old.RS) old.RS->Release();
|
||||
ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release();
|
||||
ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release();
|
||||
ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release();
|
||||
ctx->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release();
|
||||
ctx->PSSetShader(old.PS); if (old.PS) old.PS->Release();
|
||||
ctx->VSSetShader(old.VS); if (old.VS) old.VS->Release();
|
||||
ctx->GSSetShader(old.GS); if (old.GS) old.GS->Release();
|
||||
ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release();
|
||||
ctx->IASetPrimitiveTopology(old.PrimitiveTopology);
|
||||
ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release();
|
||||
ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release();
|
||||
ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release();
|
||||
}
|
||||
|
||||
static void ImGui_ImplDX10_DestroyTexture(ImTextureData* tex)
|
||||
{
|
||||
if (ImGui_ImplDX10_Texture* backend_tex = (ImGui_ImplDX10_Texture*)tex->BackendUserData)
|
||||
{
|
||||
IM_ASSERT(backend_tex->pTextureView == (ID3D10ShaderResourceView*)(intptr_t)tex->TexID);
|
||||
backend_tex->pTextureView->Release();
|
||||
backend_tex->pTexture->Release();
|
||||
IM_DELETE(backend_tex);
|
||||
|
||||
// Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running)
|
||||
tex->SetTexID(ImTextureID_Invalid);
|
||||
tex->BackendUserData = nullptr;
|
||||
}
|
||||
tex->SetStatus(ImTextureStatus_Destroyed);
|
||||
}
|
||||
|
||||
void ImGui_ImplDX10_UpdateTexture(ImTextureData* tex)
|
||||
static void ImGui_ImplDX10_CreateFontsTexture()
|
||||
{
|
||||
// Build texture atlas
|
||||
ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
|
||||
if (tex->Status == ImTextureStatus_WantCreate)
|
||||
{
|
||||
// Create and upload new texture to graphics system
|
||||
//IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height);
|
||||
IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr);
|
||||
IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
|
||||
unsigned int* pixels = (unsigned int*)tex->GetPixels();
|
||||
ImGui_ImplDX10_Texture* backend_tex = IM_NEW(ImGui_ImplDX10_Texture)();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
unsigned char* pixels;
|
||||
int width, height;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
||||
|
||||
// Create texture
|
||||
// Upload texture to graphics system
|
||||
{
|
||||
D3D10_TEXTURE2D_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.Width = (UINT)tex->Width;
|
||||
desc.Height = (UINT)tex->Height;
|
||||
desc.Width = width;
|
||||
desc.Height = height;
|
||||
desc.MipLevels = 1;
|
||||
desc.ArraySize = 1;
|
||||
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
@@ -364,12 +319,13 @@ void ImGui_ImplDX10_UpdateTexture(ImTextureData* tex)
|
||||
desc.BindFlags = D3D10_BIND_SHADER_RESOURCE;
|
||||
desc.CPUAccessFlags = 0;
|
||||
|
||||
ID3D10Texture2D* pTexture = nullptr;
|
||||
D3D10_SUBRESOURCE_DATA subResource;
|
||||
subResource.pSysMem = pixels;
|
||||
subResource.SysMemPitch = desc.Width * 4;
|
||||
subResource.SysMemSlicePitch = 0;
|
||||
bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &backend_tex->pTexture);
|
||||
IM_ASSERT(backend_tex->pTexture != nullptr && "Backend failed to create texture!");
|
||||
bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture);
|
||||
IM_ASSERT(pTexture != nullptr);
|
||||
|
||||
// Create texture view
|
||||
D3D10_SHADER_RESOURCE_VIEW_DESC srv_desc;
|
||||
@@ -378,29 +334,28 @@ void ImGui_ImplDX10_UpdateTexture(ImTextureData* tex)
|
||||
srv_desc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D;
|
||||
srv_desc.Texture2D.MipLevels = desc.MipLevels;
|
||||
srv_desc.Texture2D.MostDetailedMip = 0;
|
||||
bd->pd3dDevice->CreateShaderResourceView(backend_tex->pTexture, &srv_desc, &backend_tex->pTextureView);
|
||||
IM_ASSERT(backend_tex->pTextureView != nullptr && "Backend failed to create texture!");
|
||||
bd->pd3dDevice->CreateShaderResourceView(pTexture, &srv_desc, &bd->pFontTextureView);
|
||||
pTexture->Release();
|
||||
}
|
||||
|
||||
// Store identifiers
|
||||
tex->SetTexID((ImTextureID)(intptr_t)backend_tex->pTextureView);
|
||||
tex->SetStatus(ImTextureStatus_OK);
|
||||
tex->BackendUserData = backend_tex;
|
||||
}
|
||||
else if (tex->Status == ImTextureStatus_WantUpdates)
|
||||
// Store our identifier
|
||||
io.Fonts->SetTexID((ImTextureID)bd->pFontTextureView);
|
||||
|
||||
// Create texture sampler
|
||||
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
|
||||
{
|
||||
// Update selected blocks. We only ever write to textures regions which have never been used before!
|
||||
// This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region.
|
||||
ImGui_ImplDX10_Texture* backend_tex = (ImGui_ImplDX10_Texture*)tex->BackendUserData;
|
||||
IM_ASSERT(backend_tex->pTextureView == (ID3D10ShaderResourceView*)(intptr_t)tex->TexID);
|
||||
for (ImTextureRect& r : tex->Updates)
|
||||
{
|
||||
D3D10_BOX box = { (UINT)r.x, (UINT)r.y, (UINT)0, (UINT)(r.x + r.w), (UINT)(r.y + r.h), (UINT)1 };
|
||||
bd->pd3dDevice->UpdateSubresource(backend_tex->pTexture, 0, &box, tex->GetPixelsAt(r.x, r.y), (UINT)tex->GetPitch(), 0);
|
||||
}
|
||||
tex->SetStatus(ImTextureStatus_OK);
|
||||
D3D10_SAMPLER_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.Filter = D3D10_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
desc.AddressU = D3D10_TEXTURE_ADDRESS_WRAP;
|
||||
desc.AddressV = D3D10_TEXTURE_ADDRESS_WRAP;
|
||||
desc.AddressW = D3D10_TEXTURE_ADDRESS_WRAP;
|
||||
desc.MipLODBias = 0.f;
|
||||
desc.ComparisonFunc = D3D10_COMPARISON_ALWAYS;
|
||||
desc.MinLOD = 0.f;
|
||||
desc.MaxLOD = 0.f;
|
||||
bd->pd3dDevice->CreateSamplerState(&desc, &bd->pFontSampler);
|
||||
}
|
||||
if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0)
|
||||
ImGui_ImplDX10_DestroyTexture(tex);
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX10_CreateDeviceObjects()
|
||||
@@ -408,7 +363,8 @@ bool ImGui_ImplDX10_CreateDeviceObjects()
|
||||
ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
|
||||
if (!bd->pd3dDevice)
|
||||
return false;
|
||||
ImGui_ImplDX10_InvalidateDeviceObjects();
|
||||
if (bd->pFontSampler)
|
||||
ImGui_ImplDX10_InvalidateDeviceObjects();
|
||||
|
||||
// By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A)
|
||||
// If you would like to use this DX10 sample code but remove this dependency you can:
|
||||
@@ -471,7 +427,7 @@ bool ImGui_ImplDX10_CreateDeviceObjects()
|
||||
|
||||
// Create the constant buffer
|
||||
{
|
||||
D3D10_BUFFER_DESC desc = {};
|
||||
D3D10_BUFFER_DESC desc;
|
||||
desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER_DX10);
|
||||
desc.Usage = D3D10_USAGE_DYNAMIC;
|
||||
desc.BindFlags = D3D10_BIND_CONSTANT_BUFFER;
|
||||
@@ -551,21 +507,7 @@ bool ImGui_ImplDX10_CreateDeviceObjects()
|
||||
bd->pd3dDevice->CreateDepthStencilState(&desc, &bd->pDepthStencilState);
|
||||
}
|
||||
|
||||
// Create texture sampler
|
||||
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
|
||||
{
|
||||
D3D10_SAMPLER_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.Filter = D3D10_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
desc.AddressU = D3D10_TEXTURE_ADDRESS_CLAMP;
|
||||
desc.AddressV = D3D10_TEXTURE_ADDRESS_CLAMP;
|
||||
desc.AddressW = D3D10_TEXTURE_ADDRESS_CLAMP;
|
||||
desc.MipLODBias = 0.f;
|
||||
desc.ComparisonFunc = D3D10_COMPARISON_ALWAYS;
|
||||
desc.MinLOD = 0.f;
|
||||
desc.MaxLOD = 0.f;
|
||||
bd->pd3dDevice->CreateSamplerState(&desc, &bd->pTexSamplerLinear);
|
||||
}
|
||||
ImGui_ImplDX10_CreateFontsTexture();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -576,11 +518,8 @@ void ImGui_ImplDX10_InvalidateDeviceObjects()
|
||||
if (!bd->pd3dDevice)
|
||||
return;
|
||||
|
||||
// Destroy all textures
|
||||
for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)
|
||||
if (tex->RefCount == 1)
|
||||
ImGui_ImplDX10_DestroyTexture(tex);
|
||||
if (bd->pTexSamplerLinear) { bd->pTexSamplerLinear->Release(); bd->pTexSamplerLinear = nullptr; }
|
||||
if (bd->pFontSampler) { bd->pFontSampler->Release(); bd->pFontSampler = nullptr; }
|
||||
if (bd->pFontTextureView) { bd->pFontTextureView->Release(); bd->pFontTextureView = nullptr; ImGui::GetIO().Fonts->SetTexID(0); } // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well.
|
||||
if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; }
|
||||
if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; }
|
||||
if (bd->pBlendState) { bd->pBlendState->Release(); bd->pBlendState = nullptr; }
|
||||
@@ -603,10 +542,6 @@ bool ImGui_ImplDX10_Init(ID3D10Device* device)
|
||||
io.BackendRendererUserData = (void*)bd;
|
||||
io.BackendRendererName = "imgui_impl_dx10";
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render.
|
||||
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
platform_io.Renderer_TextureMaxWidth = platform_io.Renderer_TextureMaxHeight = D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION;
|
||||
|
||||
// Get factory from device
|
||||
IDXGIDevice* pDXGIDevice = nullptr;
|
||||
@@ -631,16 +566,13 @@ void ImGui_ImplDX10_Shutdown()
|
||||
ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
|
||||
ImGui_ImplDX10_InvalidateDeviceObjects();
|
||||
if (bd->pFactory) { bd->pFactory->Release(); }
|
||||
if (bd->pd3dDevice) { bd->pd3dDevice->Release(); }
|
||||
|
||||
io.BackendRendererName = nullptr;
|
||||
io.BackendRendererUserData = nullptr;
|
||||
io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures);
|
||||
platform_io.ClearRendererHandlers();
|
||||
io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset;
|
||||
IM_DELETE(bd);
|
||||
}
|
||||
|
||||
@@ -649,9 +581,8 @@ void ImGui_ImplDX10_NewFrame()
|
||||
ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplDX10_Init()?");
|
||||
|
||||
if (!bd->pVertexShader)
|
||||
if (!ImGui_ImplDX10_CreateDeviceObjects())
|
||||
IM_ASSERT(0 && "ImGui_ImplDX10_CreateDeviceObjects() failed!");
|
||||
if (!bd->pFontSampler)
|
||||
ImGui_ImplDX10_CreateDeviceObjects();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
+3
-20
@@ -2,9 +2,8 @@
|
||||
// This needs to be used along with a Platform Backend (e.g. Win32)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
|
||||
// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
|
||||
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
|
||||
// [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID!
|
||||
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
|
||||
|
||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
@@ -19,30 +18,14 @@
|
||||
#ifndef IMGUI_DISABLE
|
||||
|
||||
struct ID3D10Device;
|
||||
struct ID3D10SamplerState;
|
||||
struct ID3D10Buffer;
|
||||
|
||||
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX10_Init(ID3D10Device* device);
|
||||
IMGUI_IMPL_API void ImGui_ImplDX10_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplDX10_NewFrame();
|
||||
IMGUI_IMPL_API void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data);
|
||||
|
||||
// Use if you want to reset your rendering device without losing Dear ImGui state.
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX10_CreateDeviceObjects();
|
||||
IMGUI_IMPL_API void ImGui_ImplDX10_InvalidateDeviceObjects();
|
||||
|
||||
// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = NULL to handle this manually.
|
||||
IMGUI_IMPL_API void ImGui_ImplDX10_UpdateTexture(ImTextureData* tex);
|
||||
|
||||
// [BETA] Selected render state data shared with callbacks.
|
||||
// This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplDX10_RenderDrawData() call.
|
||||
// (Please open an issue if you feel you need access to more data)
|
||||
struct ImGui_ImplDX10_RenderState
|
||||
{
|
||||
ID3D10Device* Device;
|
||||
ID3D10SamplerState* SamplerDefault;
|
||||
ID3D10Buffer* VertexConstantBuffer;
|
||||
};
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX10_CreateDeviceObjects();
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
|
||||
+182
-255
@@ -2,10 +2,8 @@
|
||||
// This needs to be used along with a Platform Backend (e.g. Win32)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
|
||||
// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
|
||||
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
|
||||
// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
|
||||
// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID!
|
||||
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
|
||||
|
||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
@@ -17,18 +15,12 @@
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown.
|
||||
// 2025-06-11: DirectX11: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas.
|
||||
// 2025-05-07: DirectX11: Honor draw_data->FramebufferScale to allow for custom backends and experiment using it (consistently with other renderer backends, even though in normal condition it is not set under Windows).
|
||||
// 2025-01-06: DirectX11: Expose VertexConstantBuffer in ImGui_ImplDX11_RenderState. Reset projection matrix in ImDrawCallback_ResetRenderState handler.
|
||||
// 2024-10-07: DirectX11: Changed default texture sampler to Clamp instead of Repeat/Wrap.
|
||||
// 2024-10-07: DirectX11: Expose selected render state in ImGui_ImplDX11_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
|
||||
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
|
||||
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
|
||||
// 2021-05-19: DirectX11: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
|
||||
// 2021-02-18: DirectX11: Change blending equation to preserve alpha in output buffer.
|
||||
// 2019-08-01: DirectX11: Fixed code querying the Geometry Shader state (would generally error with Debug layer enabled).
|
||||
// 2019-07-21: DirectX11: Backup, clear and restore Geometry Shader is any is bound when calling ImGui_ImplDX11_RenderDrawData. Clearing Hull/Domain/Compute shaders without backup/restore.
|
||||
// 2019-07-21: DirectX11: Backup, clear and restore Geometry Shader is any is bound when calling ImGui_ImplDX10_RenderDrawData. Clearing Hull/Domain/Compute shaders without backup/restore.
|
||||
// 2019-05-29: DirectX11: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
|
||||
// 2019-04-30: DirectX11: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
|
||||
// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile().
|
||||
@@ -53,19 +45,7 @@
|
||||
#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
|
||||
#endif
|
||||
|
||||
// Clang/GCC warnings with -Weverything
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
|
||||
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||
#endif
|
||||
|
||||
// DirectX11 data
|
||||
struct ImGui_ImplDX11_Texture
|
||||
{
|
||||
ID3D11Texture2D* pTexture;
|
||||
ID3D11ShaderResourceView* pTextureView;
|
||||
};
|
||||
|
||||
struct ImGui_ImplDX11_Data
|
||||
{
|
||||
ID3D11Device* pd3dDevice;
|
||||
@@ -77,7 +57,8 @@ struct ImGui_ImplDX11_Data
|
||||
ID3D11InputLayout* pInputLayout;
|
||||
ID3D11Buffer* pVertexConstantBuffer;
|
||||
ID3D11PixelShader* pPixelShader;
|
||||
ID3D11SamplerState* pTexSamplerLinear;
|
||||
ID3D11SamplerState* pFontSampler;
|
||||
ID3D11ShaderResourceView* pFontTextureView;
|
||||
ID3D11RasterizerState* pRasterizerState;
|
||||
ID3D11BlendState* pBlendState;
|
||||
ID3D11DepthStencilState* pDepthStencilState;
|
||||
@@ -100,24 +81,107 @@ static ImGui_ImplDX11_Data* ImGui_ImplDX11_GetBackendData()
|
||||
}
|
||||
|
||||
// Functions
|
||||
static void ImGui_ImplDX11_SetupRenderState(const ImDrawData* draw_data, ID3D11DeviceContext* device_ctx)
|
||||
static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceContext* ctx)
|
||||
{
|
||||
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
|
||||
|
||||
// Setup viewport
|
||||
D3D11_VIEWPORT vp = {};
|
||||
vp.Width = draw_data->DisplaySize.x * draw_data->FramebufferScale.x;
|
||||
vp.Height = draw_data->DisplaySize.y * draw_data->FramebufferScale.y;
|
||||
D3D11_VIEWPORT vp;
|
||||
memset(&vp, 0, sizeof(D3D11_VIEWPORT));
|
||||
vp.Width = draw_data->DisplaySize.x;
|
||||
vp.Height = draw_data->DisplaySize.y;
|
||||
vp.MinDepth = 0.0f;
|
||||
vp.MaxDepth = 1.0f;
|
||||
vp.TopLeftX = vp.TopLeftY = 0;
|
||||
device_ctx->RSSetViewports(1, &vp);
|
||||
ctx->RSSetViewports(1, &vp);
|
||||
|
||||
// Setup shader and vertex buffers
|
||||
unsigned int stride = sizeof(ImDrawVert);
|
||||
unsigned int offset = 0;
|
||||
ctx->IASetInputLayout(bd->pInputLayout);
|
||||
ctx->IASetVertexBuffers(0, 1, &bd->pVB, &stride, &offset);
|
||||
ctx->IASetIndexBuffer(bd->pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
|
||||
ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
ctx->VSSetShader(bd->pVertexShader, nullptr, 0);
|
||||
ctx->VSSetConstantBuffers(0, 1, &bd->pVertexConstantBuffer);
|
||||
ctx->PSSetShader(bd->pPixelShader, nullptr, 0);
|
||||
ctx->PSSetSamplers(0, 1, &bd->pFontSampler);
|
||||
ctx->GSSetShader(nullptr, nullptr, 0);
|
||||
ctx->HSSetShader(nullptr, nullptr, 0); // In theory we should backup and restore this as well.. very infrequently used..
|
||||
ctx->DSSetShader(nullptr, nullptr, 0); // In theory we should backup and restore this as well.. very infrequently used..
|
||||
ctx->CSSetShader(nullptr, nullptr, 0); // In theory we should backup and restore this as well.. very infrequently used..
|
||||
|
||||
// Setup blend state
|
||||
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
|
||||
ctx->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff);
|
||||
ctx->OMSetDepthStencilState(bd->pDepthStencilState, 0);
|
||||
ctx->RSSetState(bd->pRasterizerState);
|
||||
}
|
||||
|
||||
// Render function
|
||||
void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
|
||||
{
|
||||
// Avoid rendering when minimized
|
||||
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
||||
return;
|
||||
|
||||
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
|
||||
ID3D11DeviceContext* ctx = bd->pd3dDeviceContext;
|
||||
|
||||
// Create and grow vertex/index buffers if needed
|
||||
if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount)
|
||||
{
|
||||
if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; }
|
||||
bd->VertexBufferSize = draw_data->TotalVtxCount + 5000;
|
||||
D3D11_BUFFER_DESC desc;
|
||||
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
|
||||
desc.Usage = D3D11_USAGE_DYNAMIC;
|
||||
desc.ByteWidth = bd->VertexBufferSize * sizeof(ImDrawVert);
|
||||
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
|
||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||
desc.MiscFlags = 0;
|
||||
if (bd->pd3dDevice->CreateBuffer(&desc, nullptr, &bd->pVB) < 0)
|
||||
return;
|
||||
}
|
||||
if (!bd->pIB || bd->IndexBufferSize < draw_data->TotalIdxCount)
|
||||
{
|
||||
if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; }
|
||||
bd->IndexBufferSize = draw_data->TotalIdxCount + 10000;
|
||||
D3D11_BUFFER_DESC desc;
|
||||
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
|
||||
desc.Usage = D3D11_USAGE_DYNAMIC;
|
||||
desc.ByteWidth = bd->IndexBufferSize * sizeof(ImDrawIdx);
|
||||
desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
|
||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||
if (bd->pd3dDevice->CreateBuffer(&desc, nullptr, &bd->pIB) < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// Upload vertex/index data into a single contiguous GPU buffer
|
||||
D3D11_MAPPED_SUBRESOURCE vtx_resource, idx_resource;
|
||||
if (ctx->Map(bd->pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK)
|
||||
return;
|
||||
if (ctx->Map(bd->pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK)
|
||||
return;
|
||||
ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData;
|
||||
ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData;
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
|
||||
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
||||
vtx_dst += cmd_list->VtxBuffer.Size;
|
||||
idx_dst += cmd_list->IdxBuffer.Size;
|
||||
}
|
||||
ctx->Unmap(bd->pVB, 0);
|
||||
ctx->Unmap(bd->pIB, 0);
|
||||
|
||||
// Setup orthographic projection matrix into our constant buffer
|
||||
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
|
||||
D3D11_MAPPED_SUBRESOURCE mapped_resource;
|
||||
if (device_ctx->Map(bd->pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) == S_OK)
|
||||
{
|
||||
D3D11_MAPPED_SUBRESOURCE mapped_resource;
|
||||
if (ctx->Map(bd->pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK)
|
||||
return;
|
||||
VERTEX_CONSTANT_BUFFER_DX11* constant_buffer = (VERTEX_CONSTANT_BUFFER_DX11*)mapped_resource.pData;
|
||||
float L = draw_data->DisplayPos.x;
|
||||
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
|
||||
@@ -131,94 +195,9 @@ static void ImGui_ImplDX11_SetupRenderState(const ImDrawData* draw_data, ID3D11D
|
||||
{ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
|
||||
};
|
||||
memcpy(&constant_buffer->mvp, mvp, sizeof(mvp));
|
||||
device_ctx->Unmap(bd->pVertexConstantBuffer, 0);
|
||||
ctx->Unmap(bd->pVertexConstantBuffer, 0);
|
||||
}
|
||||
|
||||
// Setup shader and vertex buffers
|
||||
unsigned int stride = sizeof(ImDrawVert);
|
||||
unsigned int offset = 0;
|
||||
device_ctx->IASetInputLayout(bd->pInputLayout);
|
||||
device_ctx->IASetVertexBuffers(0, 1, &bd->pVB, &stride, &offset);
|
||||
device_ctx->IASetIndexBuffer(bd->pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
|
||||
device_ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
device_ctx->VSSetShader(bd->pVertexShader, nullptr, 0);
|
||||
device_ctx->VSSetConstantBuffers(0, 1, &bd->pVertexConstantBuffer);
|
||||
device_ctx->PSSetShader(bd->pPixelShader, nullptr, 0);
|
||||
device_ctx->PSSetSamplers(0, 1, &bd->pTexSamplerLinear);
|
||||
device_ctx->GSSetShader(nullptr, nullptr, 0);
|
||||
device_ctx->HSSetShader(nullptr, nullptr, 0); // In theory we should backup and restore this as well.. very infrequently used..
|
||||
device_ctx->DSSetShader(nullptr, nullptr, 0); // In theory we should backup and restore this as well.. very infrequently used..
|
||||
device_ctx->CSSetShader(nullptr, nullptr, 0); // In theory we should backup and restore this as well.. very infrequently used..
|
||||
|
||||
// Setup render state
|
||||
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
|
||||
device_ctx->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff);
|
||||
device_ctx->OMSetDepthStencilState(bd->pDepthStencilState, 0);
|
||||
device_ctx->RSSetState(bd->pRasterizerState);
|
||||
}
|
||||
|
||||
// Render function
|
||||
void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
|
||||
{
|
||||
// Avoid rendering when minimized
|
||||
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
||||
return;
|
||||
|
||||
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
|
||||
ID3D11DeviceContext* device = bd->pd3dDeviceContext;
|
||||
|
||||
// Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do.
|
||||
// (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates).
|
||||
if (draw_data->Textures != nullptr)
|
||||
for (ImTextureData* tex : *draw_data->Textures)
|
||||
if (tex->Status != ImTextureStatus_OK)
|
||||
ImGui_ImplDX11_UpdateTexture(tex);
|
||||
|
||||
// Create and grow vertex/index buffers if needed
|
||||
if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount)
|
||||
{
|
||||
if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; }
|
||||
bd->VertexBufferSize = draw_data->TotalVtxCount + 5000;
|
||||
D3D11_BUFFER_DESC desc = {};
|
||||
desc.Usage = D3D11_USAGE_DYNAMIC;
|
||||
desc.ByteWidth = bd->VertexBufferSize * sizeof(ImDrawVert);
|
||||
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
|
||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||
desc.MiscFlags = 0;
|
||||
if (bd->pd3dDevice->CreateBuffer(&desc, nullptr, &bd->pVB) < 0)
|
||||
return;
|
||||
}
|
||||
if (!bd->pIB || bd->IndexBufferSize < draw_data->TotalIdxCount)
|
||||
{
|
||||
if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; }
|
||||
bd->IndexBufferSize = draw_data->TotalIdxCount + 10000;
|
||||
D3D11_BUFFER_DESC desc = {};
|
||||
desc.Usage = D3D11_USAGE_DYNAMIC;
|
||||
desc.ByteWidth = bd->IndexBufferSize * sizeof(ImDrawIdx);
|
||||
desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
|
||||
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||
if (bd->pd3dDevice->CreateBuffer(&desc, nullptr, &bd->pIB) < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// Upload vertex/index data into a single contiguous GPU buffer
|
||||
D3D11_MAPPED_SUBRESOURCE vtx_resource, idx_resource;
|
||||
if (device->Map(bd->pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK)
|
||||
return;
|
||||
if (device->Map(bd->pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK)
|
||||
return;
|
||||
ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData;
|
||||
ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData;
|
||||
for (const ImDrawList* draw_list : draw_data->CmdLists)
|
||||
{
|
||||
memcpy(vtx_dst, draw_list->VtxBuffer.Data, draw_list->VtxBuffer.Size * sizeof(ImDrawVert));
|
||||
memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
||||
vtx_dst += draw_list->VtxBuffer.Size;
|
||||
idx_dst += draw_list->IdxBuffer.Size;
|
||||
}
|
||||
device->Unmap(bd->pVB, 0);
|
||||
device->Unmap(bd->pIB, 0);
|
||||
|
||||
// Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!)
|
||||
struct BACKUP_DX11_STATE
|
||||
{
|
||||
@@ -246,132 +225,104 @@ void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
|
||||
};
|
||||
BACKUP_DX11_STATE old = {};
|
||||
old.ScissorRectsCount = old.ViewportsCount = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE;
|
||||
device->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects);
|
||||
device->RSGetViewports(&old.ViewportsCount, old.Viewports);
|
||||
device->RSGetState(&old.RS);
|
||||
device->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask);
|
||||
device->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef);
|
||||
device->PSGetShaderResources(0, 1, &old.PSShaderResource);
|
||||
device->PSGetSamplers(0, 1, &old.PSSampler);
|
||||
ctx->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects);
|
||||
ctx->RSGetViewports(&old.ViewportsCount, old.Viewports);
|
||||
ctx->RSGetState(&old.RS);
|
||||
ctx->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask);
|
||||
ctx->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef);
|
||||
ctx->PSGetShaderResources(0, 1, &old.PSShaderResource);
|
||||
ctx->PSGetSamplers(0, 1, &old.PSSampler);
|
||||
old.PSInstancesCount = old.VSInstancesCount = old.GSInstancesCount = 256;
|
||||
device->PSGetShader(&old.PS, old.PSInstances, &old.PSInstancesCount);
|
||||
device->VSGetShader(&old.VS, old.VSInstances, &old.VSInstancesCount);
|
||||
device->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer);
|
||||
device->GSGetShader(&old.GS, old.GSInstances, &old.GSInstancesCount);
|
||||
ctx->PSGetShader(&old.PS, old.PSInstances, &old.PSInstancesCount);
|
||||
ctx->VSGetShader(&old.VS, old.VSInstances, &old.VSInstancesCount);
|
||||
ctx->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer);
|
||||
ctx->GSGetShader(&old.GS, old.GSInstances, &old.GSInstancesCount);
|
||||
|
||||
device->IAGetPrimitiveTopology(&old.PrimitiveTopology);
|
||||
device->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset);
|
||||
device->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset);
|
||||
device->IAGetInputLayout(&old.InputLayout);
|
||||
ctx->IAGetPrimitiveTopology(&old.PrimitiveTopology);
|
||||
ctx->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset);
|
||||
ctx->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset);
|
||||
ctx->IAGetInputLayout(&old.InputLayout);
|
||||
|
||||
// Setup desired DX state
|
||||
ImGui_ImplDX11_SetupRenderState(draw_data, device);
|
||||
|
||||
// Setup render state structure (for callbacks and custom texture bindings)
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
ImGui_ImplDX11_RenderState render_state;
|
||||
render_state.Device = bd->pd3dDevice;
|
||||
render_state.DeviceContext = bd->pd3dDeviceContext;
|
||||
render_state.SamplerDefault = bd->pTexSamplerLinear;
|
||||
render_state.VertexConstantBuffer = bd->pVertexConstantBuffer;
|
||||
platform_io.Renderer_RenderState = &render_state;
|
||||
ImGui_ImplDX11_SetupRenderState(draw_data, ctx);
|
||||
|
||||
// Render command lists
|
||||
// (Because we merged all buffers into a single one, we maintain our own offset into them)
|
||||
int global_idx_offset = 0;
|
||||
int global_vtx_offset = 0;
|
||||
ImVec2 clip_off = draw_data->DisplayPos;
|
||||
ImVec2 clip_scale = draw_data->FramebufferScale;
|
||||
for (const ImDrawList* draw_list : draw_data->CmdLists)
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
|
||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||
if (pcmd->UserCallback != nullptr)
|
||||
{
|
||||
// User callback, registered via ImDrawList::AddCallback()
|
||||
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
|
||||
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
||||
ImGui_ImplDX11_SetupRenderState(draw_data, device);
|
||||
ImGui_ImplDX11_SetupRenderState(draw_data, ctx);
|
||||
else
|
||||
pcmd->UserCallback(draw_list, pcmd);
|
||||
pcmd->UserCallback(cmd_list, pcmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Project scissor/clipping rectangles into framebuffer space
|
||||
ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
|
||||
ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
|
||||
ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y);
|
||||
ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y);
|
||||
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
|
||||
continue;
|
||||
|
||||
// Apply scissor/clipping rectangle
|
||||
const D3D11_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y };
|
||||
device->RSSetScissorRects(1, &r);
|
||||
ctx->RSSetScissorRects(1, &r);
|
||||
|
||||
// Bind texture, Draw
|
||||
ID3D11ShaderResourceView* texture_srv = (ID3D11ShaderResourceView*)pcmd->GetTexID();
|
||||
device->PSSetShaderResources(0, 1, &texture_srv);
|
||||
device->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset);
|
||||
ctx->PSSetShaderResources(0, 1, &texture_srv);
|
||||
ctx->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset);
|
||||
}
|
||||
}
|
||||
global_idx_offset += draw_list->IdxBuffer.Size;
|
||||
global_vtx_offset += draw_list->VtxBuffer.Size;
|
||||
global_idx_offset += cmd_list->IdxBuffer.Size;
|
||||
global_vtx_offset += cmd_list->VtxBuffer.Size;
|
||||
}
|
||||
platform_io.Renderer_RenderState = nullptr;
|
||||
|
||||
// Restore modified DX state
|
||||
device->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects);
|
||||
device->RSSetViewports(old.ViewportsCount, old.Viewports);
|
||||
device->RSSetState(old.RS); if (old.RS) old.RS->Release();
|
||||
device->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release();
|
||||
device->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release();
|
||||
device->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release();
|
||||
device->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release();
|
||||
device->PSSetShader(old.PS, old.PSInstances, old.PSInstancesCount); if (old.PS) old.PS->Release();
|
||||
ctx->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects);
|
||||
ctx->RSSetViewports(old.ViewportsCount, old.Viewports);
|
||||
ctx->RSSetState(old.RS); if (old.RS) old.RS->Release();
|
||||
ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release();
|
||||
ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release();
|
||||
ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release();
|
||||
ctx->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release();
|
||||
ctx->PSSetShader(old.PS, old.PSInstances, old.PSInstancesCount); if (old.PS) old.PS->Release();
|
||||
for (UINT i = 0; i < old.PSInstancesCount; i++) if (old.PSInstances[i]) old.PSInstances[i]->Release();
|
||||
device->VSSetShader(old.VS, old.VSInstances, old.VSInstancesCount); if (old.VS) old.VS->Release();
|
||||
device->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release();
|
||||
device->GSSetShader(old.GS, old.GSInstances, old.GSInstancesCount); if (old.GS) old.GS->Release();
|
||||
ctx->VSSetShader(old.VS, old.VSInstances, old.VSInstancesCount); if (old.VS) old.VS->Release();
|
||||
ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release();
|
||||
ctx->GSSetShader(old.GS, old.GSInstances, old.GSInstancesCount); if (old.GS) old.GS->Release();
|
||||
for (UINT i = 0; i < old.VSInstancesCount; i++) if (old.VSInstances[i]) old.VSInstances[i]->Release();
|
||||
device->IASetPrimitiveTopology(old.PrimitiveTopology);
|
||||
device->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release();
|
||||
device->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release();
|
||||
device->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release();
|
||||
ctx->IASetPrimitiveTopology(old.PrimitiveTopology);
|
||||
ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release();
|
||||
ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release();
|
||||
ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release();
|
||||
}
|
||||
|
||||
static void ImGui_ImplDX11_DestroyTexture(ImTextureData* tex)
|
||||
{
|
||||
if (ImGui_ImplDX11_Texture* backend_tex = (ImGui_ImplDX11_Texture*)tex->BackendUserData)
|
||||
{
|
||||
IM_ASSERT(backend_tex->pTextureView == (ID3D11ShaderResourceView*)(intptr_t)tex->TexID);
|
||||
backend_tex->pTextureView->Release();
|
||||
backend_tex->pTexture->Release();
|
||||
IM_DELETE(backend_tex);
|
||||
|
||||
// Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running)
|
||||
tex->SetTexID(ImTextureID_Invalid);
|
||||
tex->BackendUserData = nullptr;
|
||||
}
|
||||
tex->SetStatus(ImTextureStatus_Destroyed);
|
||||
}
|
||||
|
||||
void ImGui_ImplDX11_UpdateTexture(ImTextureData* tex)
|
||||
static void ImGui_ImplDX11_CreateFontsTexture()
|
||||
{
|
||||
// Build texture atlas
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
|
||||
if (tex->Status == ImTextureStatus_WantCreate)
|
||||
{
|
||||
// Create and upload new texture to graphics system
|
||||
//IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height);
|
||||
IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr);
|
||||
IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
|
||||
unsigned int* pixels = (unsigned int*)tex->GetPixels();
|
||||
ImGui_ImplDX11_Texture* backend_tex = IM_NEW(ImGui_ImplDX11_Texture)();
|
||||
unsigned char* pixels;
|
||||
int width, height;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
||||
|
||||
// Create texture
|
||||
// Upload texture to graphics system
|
||||
{
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.Width = (UINT)tex->Width;
|
||||
desc.Height = (UINT)tex->Height;
|
||||
desc.Width = width;
|
||||
desc.Height = height;
|
||||
desc.MipLevels = 1;
|
||||
desc.ArraySize = 1;
|
||||
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
@@ -379,12 +330,14 @@ void ImGui_ImplDX11_UpdateTexture(ImTextureData* tex)
|
||||
desc.Usage = D3D11_USAGE_DEFAULT;
|
||||
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
||||
desc.CPUAccessFlags = 0;
|
||||
|
||||
ID3D11Texture2D* pTexture = nullptr;
|
||||
D3D11_SUBRESOURCE_DATA subResource;
|
||||
subResource.pSysMem = pixels;
|
||||
subResource.SysMemPitch = desc.Width * 4;
|
||||
subResource.SysMemSlicePitch = 0;
|
||||
bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &backend_tex->pTexture);
|
||||
IM_ASSERT(backend_tex->pTexture != nullptr && "Backend failed to create texture!");
|
||||
bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture);
|
||||
IM_ASSERT(pTexture != nullptr);
|
||||
|
||||
// Create texture view
|
||||
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
|
||||
@@ -393,29 +346,28 @@ void ImGui_ImplDX11_UpdateTexture(ImTextureData* tex)
|
||||
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
|
||||
srvDesc.Texture2D.MipLevels = desc.MipLevels;
|
||||
srvDesc.Texture2D.MostDetailedMip = 0;
|
||||
bd->pd3dDevice->CreateShaderResourceView(backend_tex->pTexture, &srvDesc, &backend_tex->pTextureView);
|
||||
IM_ASSERT(backend_tex->pTextureView != nullptr && "Backend failed to create texture!");
|
||||
bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &bd->pFontTextureView);
|
||||
pTexture->Release();
|
||||
}
|
||||
|
||||
// Store identifiers
|
||||
tex->SetTexID((ImTextureID)(intptr_t)backend_tex->pTextureView);
|
||||
tex->SetStatus(ImTextureStatus_OK);
|
||||
tex->BackendUserData = backend_tex;
|
||||
}
|
||||
else if (tex->Status == ImTextureStatus_WantUpdates)
|
||||
// Store our identifier
|
||||
io.Fonts->SetTexID((ImTextureID)bd->pFontTextureView);
|
||||
|
||||
// Create texture sampler
|
||||
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
|
||||
{
|
||||
// Update selected blocks. We only ever write to textures regions which have never been used before!
|
||||
// This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region.
|
||||
ImGui_ImplDX11_Texture* backend_tex = (ImGui_ImplDX11_Texture*)tex->BackendUserData;
|
||||
IM_ASSERT(backend_tex->pTextureView == (ID3D11ShaderResourceView*)(intptr_t)tex->TexID);
|
||||
for (ImTextureRect& r : tex->Updates)
|
||||
{
|
||||
D3D11_BOX box = { (UINT)r.x, (UINT)r.y, (UINT)0, (UINT)(r.x + r.w), (UINT)(r.y + r .h), (UINT)1 };
|
||||
bd->pd3dDeviceContext->UpdateSubresource(backend_tex->pTexture, 0, &box, tex->GetPixelsAt(r.x, r.y), (UINT)tex->GetPitch(), 0);
|
||||
}
|
||||
tex->SetStatus(ImTextureStatus_OK);
|
||||
D3D11_SAMPLER_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||
desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||
desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
|
||||
desc.MipLODBias = 0.f;
|
||||
desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
|
||||
desc.MinLOD = 0.f;
|
||||
desc.MaxLOD = 0.f;
|
||||
bd->pd3dDevice->CreateSamplerState(&desc, &bd->pFontSampler);
|
||||
}
|
||||
if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0)
|
||||
ImGui_ImplDX11_DestroyTexture(tex);
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX11_CreateDeviceObjects()
|
||||
@@ -423,7 +375,8 @@ bool ImGui_ImplDX11_CreateDeviceObjects()
|
||||
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
|
||||
if (!bd->pd3dDevice)
|
||||
return false;
|
||||
ImGui_ImplDX11_InvalidateDeviceObjects();
|
||||
if (bd->pFontSampler)
|
||||
ImGui_ImplDX11_InvalidateDeviceObjects();
|
||||
|
||||
// By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A)
|
||||
// If you would like to use this DX11 sample code but remove this dependency you can:
|
||||
@@ -486,7 +439,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects()
|
||||
|
||||
// Create the constant buffer
|
||||
{
|
||||
D3D11_BUFFER_DESC desc = {};
|
||||
D3D11_BUFFER_DESC desc;
|
||||
desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER_DX11);
|
||||
desc.Usage = D3D11_USAGE_DYNAMIC;
|
||||
desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
|
||||
@@ -566,21 +519,7 @@ bool ImGui_ImplDX11_CreateDeviceObjects()
|
||||
bd->pd3dDevice->CreateDepthStencilState(&desc, &bd->pDepthStencilState);
|
||||
}
|
||||
|
||||
// Create texture sampler
|
||||
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
|
||||
{
|
||||
D3D11_SAMPLER_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
|
||||
desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
|
||||
desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
|
||||
desc.MipLODBias = 0.f;
|
||||
desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
|
||||
desc.MinLOD = 0.f;
|
||||
desc.MaxLOD = 0.f;
|
||||
bd->pd3dDevice->CreateSamplerState(&desc, &bd->pTexSamplerLinear);
|
||||
}
|
||||
ImGui_ImplDX11_CreateFontsTexture();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -591,12 +530,8 @@ void ImGui_ImplDX11_InvalidateDeviceObjects()
|
||||
if (!bd->pd3dDevice)
|
||||
return;
|
||||
|
||||
// Destroy all textures
|
||||
for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)
|
||||
if (tex->RefCount == 1)
|
||||
ImGui_ImplDX11_DestroyTexture(tex);
|
||||
|
||||
if (bd->pTexSamplerLinear) { bd->pTexSamplerLinear->Release(); bd->pTexSamplerLinear = nullptr; }
|
||||
if (bd->pFontSampler) { bd->pFontSampler->Release(); bd->pFontSampler = nullptr; }
|
||||
if (bd->pFontTextureView) { bd->pFontTextureView->Release(); bd->pFontTextureView = nullptr; ImGui::GetIO().Fonts->SetTexID(0); } // We copied data->pFontTextureView to io.Fonts->TexID so let's clear that as well.
|
||||
if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; }
|
||||
if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; }
|
||||
if (bd->pBlendState) { bd->pBlendState->Release(); bd->pBlendState = nullptr; }
|
||||
@@ -619,10 +554,6 @@ bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_co
|
||||
io.BackendRendererUserData = (void*)bd;
|
||||
io.BackendRendererName = "imgui_impl_dx11";
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render.
|
||||
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
platform_io.Renderer_TextureMaxWidth = platform_io.Renderer_TextureMaxHeight = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
|
||||
|
||||
// Get factory from device
|
||||
IDXGIDevice* pDXGIDevice = nullptr;
|
||||
@@ -650,17 +581,14 @@ void ImGui_ImplDX11_Shutdown()
|
||||
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
|
||||
ImGui_ImplDX11_InvalidateDeviceObjects();
|
||||
if (bd->pFactory) { bd->pFactory->Release(); }
|
||||
if (bd->pd3dDevice) { bd->pd3dDevice->Release(); }
|
||||
if (bd->pd3dDeviceContext) { bd->pd3dDeviceContext->Release(); }
|
||||
|
||||
io.BackendRendererName = nullptr;
|
||||
io.BackendRendererUserData = nullptr;
|
||||
io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures);
|
||||
platform_io.ClearRendererHandlers();
|
||||
io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset;
|
||||
IM_DELETE(bd);
|
||||
}
|
||||
|
||||
@@ -669,9 +597,8 @@ void ImGui_ImplDX11_NewFrame()
|
||||
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplDX11_Init()?");
|
||||
|
||||
if (!bd->pVertexShader)
|
||||
if (!ImGui_ImplDX11_CreateDeviceObjects())
|
||||
IM_ASSERT(0 && "ImGui_ImplDX11_CreateDeviceObjects() failed!");
|
||||
if (!bd->pFontSampler)
|
||||
ImGui_ImplDX11_CreateDeviceObjects();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
+3
-22
@@ -2,10 +2,8 @@
|
||||
// This needs to be used along with a Platform Backend (e.g. Win32)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
|
||||
// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
|
||||
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
|
||||
// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
|
||||
// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID!
|
||||
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
|
||||
|
||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
@@ -21,31 +19,14 @@
|
||||
|
||||
struct ID3D11Device;
|
||||
struct ID3D11DeviceContext;
|
||||
struct ID3D11SamplerState;
|
||||
struct ID3D11Buffer;
|
||||
|
||||
// Follow "Getting Started" link and check examples/ folder to learn about using backends!
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context);
|
||||
IMGUI_IMPL_API void ImGui_ImplDX11_Shutdown();
|
||||
IMGUI_IMPL_API void ImGui_ImplDX11_NewFrame();
|
||||
IMGUI_IMPL_API void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data);
|
||||
|
||||
// Use if you want to reset your rendering device without losing Dear ImGui state.
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX11_CreateDeviceObjects();
|
||||
IMGUI_IMPL_API void ImGui_ImplDX11_InvalidateDeviceObjects();
|
||||
|
||||
// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = NULL to handle this manually.
|
||||
IMGUI_IMPL_API void ImGui_ImplDX11_UpdateTexture(ImTextureData* tex);
|
||||
|
||||
// [BETA] Selected render state data shared with callbacks.
|
||||
// This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplDX11_RenderDrawData() call.
|
||||
// (Please open an issue if you feel you need access to more data)
|
||||
struct ImGui_ImplDX11_RenderState
|
||||
{
|
||||
ID3D11Device* Device;
|
||||
ID3D11DeviceContext* DeviceContext;
|
||||
ID3D11SamplerState* SamplerDefault;
|
||||
ID3D11Buffer* VertexConstantBuffer;
|
||||
};
|
||||
IMGUI_IMPL_API bool ImGui_ImplDX11_CreateDeviceObjects();
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
|
||||
+224
-433
@@ -2,13 +2,16 @@
|
||||
// This needs to be used along with a Platform Backend (e.g. Win32)
|
||||
|
||||
// Implemented features:
|
||||
// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
|
||||
// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
|
||||
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
|
||||
// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
|
||||
// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID!
|
||||
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
|
||||
|
||||
// The aim of imgui_impl_dx12.h/.cpp is to be usable in your engine without any modification.
|
||||
// IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/
|
||||
// Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'.
|
||||
// This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*.
|
||||
// To build this on 32-bit systems:
|
||||
// - [Solution 1] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'ImTextureID=ImU64' (this is what we do in the 'example_win32_direct12/example_win32_direct12.vcxproj' project file)
|
||||
// - [Solution 2] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'IMGUI_USER_CONFIG="my_imgui_config.h"' and inside 'my_imgui_config.h' add '#define ImTextureID ImU64' and as many other options as you like.
|
||||
// - [Solution 3] IDE/msbuild: edit imconfig.h and add '#define ImTextureID ImU64' (prefer solution 2 to create your own config file!)
|
||||
// - [Solution 4] command-line: add '/D ImTextureID=ImU64' to your cl.exe command-line (this is what we do in the example_win32_direct12/build_win32.bat file)
|
||||
|
||||
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
|
||||
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
|
||||
@@ -20,23 +23,6 @@
|
||||
|
||||
// CHANGELOG
|
||||
// (minor and older changes stripped away, please see git history for details)
|
||||
// 2025-10-11: DirectX12: Reuse texture upload buffer and grow it only when necessary. (#9002)
|
||||
// 2025-09-29: DirectX12: Rework synchronization logic. (#8961)
|
||||
// 2025-09-29: DirectX12: Enable swapchain tearing to eliminate viewports framerate throttling. (#8965)
|
||||
// 2025-09-29: DirectX12: Reuse a command list and allocator for texture uploads instead of recreating them each time. (#8963)
|
||||
// 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown.
|
||||
// 2025-06-19: Fixed build on MinGW. (#8702, #4594)
|
||||
// 2025-06-11: DirectX12: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas.
|
||||
// 2025-05-07: DirectX12: Honor draw_data->FramebufferScale to allow for custom backends and experiment using it (consistently with other renderer backends, even though in normal condition it is not set under Windows).
|
||||
// 2025-02-24: DirectX12: Fixed an issue where ImGui_ImplDX12_Init() signature change from 2024-11-15 combined with change from 2025-01-15 made legacy ImGui_ImplDX12_Init() crash. (#8429)
|
||||
// 2025-01-15: DirectX12: Texture upload use the command queue provided in ImGui_ImplDX12_InitInfo instead of creating its own.
|
||||
// 2024-12-09: DirectX12: Let user specifies the DepthStencilView format by setting ImGui_ImplDX12_InitInfo::DSVFormat.
|
||||
// 2024-11-15: DirectX12: *BREAKING CHANGE* Changed ImGui_ImplDX12_Init() signature to take a ImGui_ImplDX12_InitInfo struct. Legacy ImGui_ImplDX12_Init() signature is still supported (will obsolete).
|
||||
// 2024-11-15: DirectX12: *BREAKING CHANGE* User is now required to pass function pointers to allocate/free SRV Descriptors. We provide convenience legacy fields to pass a single descriptor, matching the old API, but upcoming features will want multiple.
|
||||
// 2024-10-23: DirectX12: Unmap() call specify written range. The range is informational and may be used by debug tools.
|
||||
// 2024-10-07: DirectX12: Changed default texture sampler to Clamp instead of Repeat/Wrap.
|
||||
// 2024-10-07: DirectX12: Expose selected render state in ImGui_ImplDX12_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
|
||||
// 2024-10-07: DirectX12: Compiling with '#define ImTextureID=ImU64' is unnecessary now that dear imgui defaults ImTextureID to u64 instead of void*.
|
||||
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
|
||||
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
|
||||
// 2021-05-19: DirectX12: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
|
||||
@@ -61,62 +47,30 @@
|
||||
|
||||
// DirectX
|
||||
#include <d3d12.h>
|
||||
#include <dxgi1_5.h>
|
||||
#include <dxgi1_4.h>
|
||||
#include <d3dcompiler.h>
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
|
||||
#endif
|
||||
|
||||
// Clang/GCC warnings with -Weverything
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
|
||||
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||
#endif
|
||||
|
||||
// MinGW workaround, see #4594
|
||||
typedef decltype(D3D12SerializeRootSignature) *_PFN_D3D12_SERIALIZE_ROOT_SIGNATURE;
|
||||
|
||||
// DirectX12 data
|
||||
// DirectX data
|
||||
struct ImGui_ImplDX12_RenderBuffers;
|
||||
|
||||
struct ImGui_ImplDX12_Texture
|
||||
{
|
||||
ID3D12Resource* pTextureResource;
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE hFontSrvCpuDescHandle;
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE hFontSrvGpuDescHandle;
|
||||
|
||||
ImGui_ImplDX12_Texture() { memset((void*)this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
struct ImGui_ImplDX12_Data
|
||||
{
|
||||
ImGui_ImplDX12_InitInfo InitInfo;
|
||||
IDXGIFactory5* pdxgiFactory;
|
||||
ID3D12Device* pd3dDevice;
|
||||
ID3D12RootSignature* pRootSignature;
|
||||
ID3D12PipelineState* pPipelineState;
|
||||
ID3D12CommandQueue* pCommandQueue;
|
||||
bool commandQueueOwned;
|
||||
DXGI_FORMAT RTVFormat;
|
||||
DXGI_FORMAT DSVFormat;
|
||||
ID3D12Resource* pFontTextureResource;
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE hFontSrvCpuDescHandle;
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE hFontSrvGpuDescHandle;
|
||||
ID3D12DescriptorHeap* pd3dSrvDescHeap;
|
||||
ID3D12Fence* Fence;
|
||||
UINT64 FenceLastSignaledValue;
|
||||
HANDLE FenceEvent;
|
||||
UINT numFramesInFlight;
|
||||
bool tearingSupport;
|
||||
bool LegacySingleDescriptorUsed;
|
||||
|
||||
ID3D12CommandAllocator* pTexCmdAllocator;
|
||||
ID3D12GraphicsCommandList* pTexCmdList;
|
||||
ID3D12Resource* pTexUploadBuffer;
|
||||
UINT pTexUploadBufferSize;
|
||||
void* pTexUploadBufferMapped;
|
||||
|
||||
ImGui_ImplDX12_RenderBuffers* pFrameResources;
|
||||
UINT frameIndex;
|
||||
|
||||
ImGui_ImplDX12_Data() { memset((void*)this, 0, sizeof(*this)); }
|
||||
ImGui_ImplDX12_Data() { memset((void*)this, 0, sizeof(*this)); frameIndex = UINT_MAX; }
|
||||
};
|
||||
|
||||
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
|
||||
@@ -141,7 +95,7 @@ struct VERTEX_CONSTANT_BUFFER_DX12
|
||||
};
|
||||
|
||||
// Functions
|
||||
static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* command_list, ImGui_ImplDX12_RenderBuffers* fr)
|
||||
static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx, ImGui_ImplDX12_RenderBuffers* fr)
|
||||
{
|
||||
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
||||
|
||||
@@ -164,35 +118,38 @@ static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12Graphic
|
||||
}
|
||||
|
||||
// Setup viewport
|
||||
D3D12_VIEWPORT vp = {};
|
||||
vp.Width = draw_data->DisplaySize.x * draw_data->FramebufferScale.x;
|
||||
vp.Height = draw_data->DisplaySize.y * draw_data->FramebufferScale.y;
|
||||
D3D12_VIEWPORT vp;
|
||||
memset(&vp, 0, sizeof(D3D12_VIEWPORT));
|
||||
vp.Width = draw_data->DisplaySize.x;
|
||||
vp.Height = draw_data->DisplaySize.y;
|
||||
vp.MinDepth = 0.0f;
|
||||
vp.MaxDepth = 1.0f;
|
||||
vp.TopLeftX = vp.TopLeftY = 0.0f;
|
||||
command_list->RSSetViewports(1, &vp);
|
||||
ctx->RSSetViewports(1, &vp);
|
||||
|
||||
// Bind shader and vertex buffers
|
||||
unsigned int stride = sizeof(ImDrawVert);
|
||||
unsigned int offset = 0;
|
||||
D3D12_VERTEX_BUFFER_VIEW vbv = {};
|
||||
D3D12_VERTEX_BUFFER_VIEW vbv;
|
||||
memset(&vbv, 0, sizeof(D3D12_VERTEX_BUFFER_VIEW));
|
||||
vbv.BufferLocation = fr->VertexBuffer->GetGPUVirtualAddress() + offset;
|
||||
vbv.SizeInBytes = fr->VertexBufferSize * stride;
|
||||
vbv.StrideInBytes = stride;
|
||||
command_list->IASetVertexBuffers(0, 1, &vbv);
|
||||
D3D12_INDEX_BUFFER_VIEW ibv = {};
|
||||
ctx->IASetVertexBuffers(0, 1, &vbv);
|
||||
D3D12_INDEX_BUFFER_VIEW ibv;
|
||||
memset(&ibv, 0, sizeof(D3D12_INDEX_BUFFER_VIEW));
|
||||
ibv.BufferLocation = fr->IndexBuffer->GetGPUVirtualAddress();
|
||||
ibv.SizeInBytes = fr->IndexBufferSize * sizeof(ImDrawIdx);
|
||||
ibv.Format = sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT;
|
||||
command_list->IASetIndexBuffer(&ibv);
|
||||
command_list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
command_list->SetPipelineState(bd->pPipelineState);
|
||||
command_list->SetGraphicsRootSignature(bd->pRootSignature);
|
||||
command_list->SetGraphicsRoot32BitConstants(0, 16, &vertex_constant_buffer, 0);
|
||||
ctx->IASetIndexBuffer(&ibv);
|
||||
ctx->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||
ctx->SetPipelineState(bd->pPipelineState);
|
||||
ctx->SetGraphicsRootSignature(bd->pRootSignature);
|
||||
ctx->SetGraphicsRoot32BitConstants(0, 16, &vertex_constant_buffer, 0);
|
||||
|
||||
// Setup blend factor
|
||||
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
|
||||
command_list->OMSetBlendFactor(blend_factor);
|
||||
ctx->OMSetBlendFactor(blend_factor);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -204,20 +161,14 @@ static inline void SafeRelease(T*& res)
|
||||
}
|
||||
|
||||
// Render function
|
||||
void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* command_list)
|
||||
void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx)
|
||||
{
|
||||
// Avoid rendering when minimized
|
||||
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
|
||||
return;
|
||||
|
||||
// Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do.
|
||||
// (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates).
|
||||
if (draw_data->Textures != nullptr)
|
||||
for (ImTextureData* tex : *draw_data->Textures)
|
||||
if (tex->Status != ImTextureStatus_OK)
|
||||
ImGui_ImplDX12_UpdateTexture(tex);
|
||||
|
||||
// FIXME: We are assuming that this only gets called once per frame!
|
||||
// FIXME: I'm assuming that this only gets called once per frame!
|
||||
// If not, we can't just re-allocate the IB or VB, we'll have to do a proper allocator.
|
||||
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
||||
bd->frameIndex = bd->frameIndex + 1;
|
||||
ImGui_ImplDX12_RenderBuffers* fr = &bd->pFrameResources[bd->frameIndex % bd->numFramesInFlight];
|
||||
@@ -227,11 +178,13 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL
|
||||
{
|
||||
SafeRelease(fr->VertexBuffer);
|
||||
fr->VertexBufferSize = draw_data->TotalVtxCount + 5000;
|
||||
D3D12_HEAP_PROPERTIES props = {};
|
||||
D3D12_HEAP_PROPERTIES props;
|
||||
memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES));
|
||||
props.Type = D3D12_HEAP_TYPE_UPLOAD;
|
||||
props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
D3D12_RESOURCE_DESC desc = {};
|
||||
D3D12_RESOURCE_DESC desc;
|
||||
memset(&desc, 0, sizeof(D3D12_RESOURCE_DESC));
|
||||
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||
desc.Width = fr->VertexBufferSize * sizeof(ImDrawVert);
|
||||
desc.Height = 1;
|
||||
@@ -248,11 +201,13 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL
|
||||
{
|
||||
SafeRelease(fr->IndexBuffer);
|
||||
fr->IndexBufferSize = draw_data->TotalIdxCount + 10000;
|
||||
D3D12_HEAP_PROPERTIES props = {};
|
||||
D3D12_HEAP_PROPERTIES props;
|
||||
memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES));
|
||||
props.Type = D3D12_HEAP_TYPE_UPLOAD;
|
||||
props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
D3D12_RESOURCE_DESC desc = {};
|
||||
D3D12_RESOURCE_DESC desc;
|
||||
memset(&desc, 0, sizeof(D3D12_RESOURCE_DESC));
|
||||
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||
desc.Width = fr->IndexBufferSize * sizeof(ImDrawIdx);
|
||||
desc.Height = 1;
|
||||
@@ -267,120 +222,84 @@ void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandL
|
||||
}
|
||||
|
||||
// Upload vertex/index data into a single contiguous GPU buffer
|
||||
// During Map() we specify a null read range (as per DX12 API, this is informational and for tooling only)
|
||||
void* vtx_resource, *idx_resource;
|
||||
D3D12_RANGE range = { 0, 0 };
|
||||
D3D12_RANGE range;
|
||||
memset(&range, 0, sizeof(D3D12_RANGE));
|
||||
if (fr->VertexBuffer->Map(0, &range, &vtx_resource) != S_OK)
|
||||
return;
|
||||
if (fr->IndexBuffer->Map(0, &range, &idx_resource) != S_OK)
|
||||
return;
|
||||
ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource;
|
||||
ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource;
|
||||
for (const ImDrawList* draw_list : draw_data->CmdLists)
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
memcpy(vtx_dst, draw_list->VtxBuffer.Data, draw_list->VtxBuffer.Size * sizeof(ImDrawVert));
|
||||
memcpy(idx_dst, draw_list->IdxBuffer.Data, draw_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
||||
vtx_dst += draw_list->VtxBuffer.Size;
|
||||
idx_dst += draw_list->IdxBuffer.Size;
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
|
||||
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
|
||||
vtx_dst += cmd_list->VtxBuffer.Size;
|
||||
idx_dst += cmd_list->IdxBuffer.Size;
|
||||
}
|
||||
|
||||
// During Unmap() we specify the written range (as per DX12 API, this is informational and for tooling only)
|
||||
range.End = (SIZE_T)((intptr_t)vtx_dst - (intptr_t)vtx_resource);
|
||||
IM_ASSERT(range.End == draw_data->TotalVtxCount * sizeof(ImDrawVert));
|
||||
fr->VertexBuffer->Unmap(0, &range);
|
||||
range.End = (SIZE_T)((intptr_t)idx_dst - (intptr_t)idx_resource);
|
||||
IM_ASSERT(range.End == draw_data->TotalIdxCount * sizeof(ImDrawIdx));
|
||||
fr->IndexBuffer->Unmap(0, &range);
|
||||
|
||||
// Setup desired DX state
|
||||
ImGui_ImplDX12_SetupRenderState(draw_data, command_list, fr);
|
||||
|
||||
// Setup render state structure (for callbacks and custom texture bindings)
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
ImGui_ImplDX12_RenderState render_state;
|
||||
render_state.Device = bd->pd3dDevice;
|
||||
render_state.CommandList = command_list;
|
||||
platform_io.Renderer_RenderState = &render_state;
|
||||
ImGui_ImplDX12_SetupRenderState(draw_data, ctx, fr);
|
||||
|
||||
// Render command lists
|
||||
// (Because we merged all buffers into a single one, we maintain our own offset into them)
|
||||
int global_vtx_offset = 0;
|
||||
int global_idx_offset = 0;
|
||||
ImVec2 clip_off = draw_data->DisplayPos;
|
||||
ImVec2 clip_scale = draw_data->FramebufferScale;
|
||||
for (const ImDrawList* draw_list : draw_data->CmdLists)
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
|
||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||
if (pcmd->UserCallback != nullptr)
|
||||
{
|
||||
// User callback, registered via ImDrawList::AddCallback()
|
||||
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
|
||||
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
|
||||
ImGui_ImplDX12_SetupRenderState(draw_data, command_list, fr);
|
||||
ImGui_ImplDX12_SetupRenderState(draw_data, ctx, fr);
|
||||
else
|
||||
pcmd->UserCallback(draw_list, pcmd);
|
||||
pcmd->UserCallback(cmd_list, pcmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Project scissor/clipping rectangles into framebuffer space
|
||||
ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
|
||||
ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
|
||||
ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y);
|
||||
ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y);
|
||||
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
|
||||
continue;
|
||||
|
||||
// Apply scissor/clipping rectangle
|
||||
// Apply Scissor/clipping rectangle, Bind texture, Draw
|
||||
const D3D12_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y };
|
||||
command_list->RSSetScissorRects(1, &r);
|
||||
|
||||
// Bind texture, Draw
|
||||
D3D12_GPU_DESCRIPTOR_HANDLE texture_handle = {};
|
||||
texture_handle.ptr = (UINT64)pcmd->GetTexID();
|
||||
command_list->SetGraphicsRootDescriptorTable(1, texture_handle);
|
||||
command_list->DrawIndexedInstanced(pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
|
||||
ctx->SetGraphicsRootDescriptorTable(1, texture_handle);
|
||||
ctx->RSSetScissorRects(1, &r);
|
||||
ctx->DrawIndexedInstanced(pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
|
||||
}
|
||||
}
|
||||
global_idx_offset += draw_list->IdxBuffer.Size;
|
||||
global_vtx_offset += draw_list->VtxBuffer.Size;
|
||||
global_idx_offset += cmd_list->IdxBuffer.Size;
|
||||
global_vtx_offset += cmd_list->VtxBuffer.Size;
|
||||
}
|
||||
platform_io.Renderer_RenderState = nullptr;
|
||||
}
|
||||
|
||||
static void ImGui_ImplDX12_DestroyTexture(ImTextureData* tex)
|
||||
{
|
||||
if (ImGui_ImplDX12_Texture* backend_tex = (ImGui_ImplDX12_Texture*)tex->BackendUserData)
|
||||
{
|
||||
IM_ASSERT(backend_tex->hFontSrvGpuDescHandle.ptr == (UINT64)tex->TexID);
|
||||
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
||||
bd->InitInfo.SrvDescriptorFreeFn(&bd->InitInfo, backend_tex->hFontSrvCpuDescHandle, backend_tex->hFontSrvGpuDescHandle);
|
||||
SafeRelease(backend_tex->pTextureResource);
|
||||
backend_tex->hFontSrvCpuDescHandle.ptr = 0;
|
||||
backend_tex->hFontSrvGpuDescHandle.ptr = 0;
|
||||
IM_DELETE(backend_tex);
|
||||
|
||||
// Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running)
|
||||
tex->SetTexID(ImTextureID_Invalid);
|
||||
tex->BackendUserData = nullptr;
|
||||
}
|
||||
tex->SetStatus(ImTextureStatus_Destroyed);
|
||||
}
|
||||
|
||||
void ImGui_ImplDX12_UpdateTexture(ImTextureData* tex)
|
||||
static void ImGui_ImplDX12_CreateFontsTexture()
|
||||
{
|
||||
// Build texture atlas
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
||||
bool need_barrier_before_copy = true; // Do we need a resource barrier before we copy new data in?
|
||||
unsigned char* pixels;
|
||||
int width, height;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
||||
|
||||
if (tex->Status == ImTextureStatus_WantCreate)
|
||||
// Upload texture to graphics system
|
||||
{
|
||||
// Create and upload new texture to graphics system
|
||||
//IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height);
|
||||
IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr);
|
||||
IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
|
||||
ImGui_ImplDX12_Texture* backend_tex = IM_NEW(ImGui_ImplDX12_Texture)();
|
||||
bd->InitInfo.SrvDescriptorAllocFn(&bd->InitInfo, &backend_tex->hFontSrvCpuDescHandle, &backend_tex->hFontSrvGpuDescHandle); // Allocate a desctriptor handle
|
||||
|
||||
D3D12_HEAP_PROPERTIES props = {};
|
||||
D3D12_HEAP_PROPERTIES props;
|
||||
memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES));
|
||||
props.Type = D3D12_HEAP_TYPE_DEFAULT;
|
||||
props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
@@ -389,8 +308,8 @@ void ImGui_ImplDX12_UpdateTexture(ImTextureData* tex)
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
||||
desc.Alignment = 0;
|
||||
desc.Width = tex->Width;
|
||||
desc.Height = tex->Height;
|
||||
desc.Width = width;
|
||||
desc.Height = height;
|
||||
desc.DepthOrArraySize = 1;
|
||||
desc.MipLevels = 1;
|
||||
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
@@ -403,7 +322,104 @@ void ImGui_ImplDX12_UpdateTexture(ImTextureData* tex)
|
||||
bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc,
|
||||
D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&pTexture));
|
||||
|
||||
// Create SRV
|
||||
UINT uploadPitch = (width * 4 + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u);
|
||||
UINT uploadSize = height * uploadPitch;
|
||||
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||
desc.Alignment = 0;
|
||||
desc.Width = uploadSize;
|
||||
desc.Height = 1;
|
||||
desc.DepthOrArraySize = 1;
|
||||
desc.MipLevels = 1;
|
||||
desc.Format = DXGI_FORMAT_UNKNOWN;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.SampleDesc.Quality = 0;
|
||||
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
||||
|
||||
props.Type = D3D12_HEAP_TYPE_UPLOAD;
|
||||
props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
|
||||
ID3D12Resource* uploadBuffer = nullptr;
|
||||
HRESULT hr = bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc,
|
||||
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uploadBuffer));
|
||||
IM_ASSERT(SUCCEEDED(hr));
|
||||
|
||||
void* mapped = nullptr;
|
||||
D3D12_RANGE range = { 0, uploadSize };
|
||||
hr = uploadBuffer->Map(0, &range, &mapped);
|
||||
IM_ASSERT(SUCCEEDED(hr));
|
||||
for (int y = 0; y < height; y++)
|
||||
memcpy((void*) ((uintptr_t) mapped + y * uploadPitch), pixels + y * width * 4, width * 4);
|
||||
uploadBuffer->Unmap(0, &range);
|
||||
|
||||
D3D12_TEXTURE_COPY_LOCATION srcLocation = {};
|
||||
srcLocation.pResource = uploadBuffer;
|
||||
srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
||||
srcLocation.PlacedFootprint.Footprint.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
srcLocation.PlacedFootprint.Footprint.Width = width;
|
||||
srcLocation.PlacedFootprint.Footprint.Height = height;
|
||||
srcLocation.PlacedFootprint.Footprint.Depth = 1;
|
||||
srcLocation.PlacedFootprint.Footprint.RowPitch = uploadPitch;
|
||||
|
||||
D3D12_TEXTURE_COPY_LOCATION dstLocation = {};
|
||||
dstLocation.pResource = pTexture;
|
||||
dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
||||
dstLocation.SubresourceIndex = 0;
|
||||
|
||||
D3D12_RESOURCE_BARRIER barrier = {};
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = pTexture;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
||||
|
||||
ID3D12Fence* fence = nullptr;
|
||||
hr = bd->pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence));
|
||||
IM_ASSERT(SUCCEEDED(hr));
|
||||
|
||||
HANDLE event = CreateEvent(0, 0, 0, 0);
|
||||
IM_ASSERT(event != nullptr);
|
||||
|
||||
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
|
||||
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
||||
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
||||
queueDesc.NodeMask = 1;
|
||||
|
||||
ID3D12CommandQueue* cmdQueue = nullptr;
|
||||
hr = bd->pd3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&cmdQueue));
|
||||
IM_ASSERT(SUCCEEDED(hr));
|
||||
|
||||
ID3D12CommandAllocator* cmdAlloc = nullptr;
|
||||
hr = bd->pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&cmdAlloc));
|
||||
IM_ASSERT(SUCCEEDED(hr));
|
||||
|
||||
ID3D12GraphicsCommandList* cmdList = nullptr;
|
||||
hr = bd->pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, cmdAlloc, nullptr, IID_PPV_ARGS(&cmdList));
|
||||
IM_ASSERT(SUCCEEDED(hr));
|
||||
|
||||
cmdList->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, nullptr);
|
||||
cmdList->ResourceBarrier(1, &barrier);
|
||||
|
||||
hr = cmdList->Close();
|
||||
IM_ASSERT(SUCCEEDED(hr));
|
||||
|
||||
cmdQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmdList);
|
||||
hr = cmdQueue->Signal(fence, 1);
|
||||
IM_ASSERT(SUCCEEDED(hr));
|
||||
|
||||
fence->SetEventOnCompletion(1, event);
|
||||
WaitForSingleObject(event, INFINITE);
|
||||
|
||||
cmdList->Release();
|
||||
cmdAlloc->Release();
|
||||
cmdQueue->Release();
|
||||
CloseHandle(event);
|
||||
fence->Release();
|
||||
uploadBuffer->Release();
|
||||
|
||||
// Create texture view
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc;
|
||||
ZeroMemory(&srvDesc, sizeof(srvDesc));
|
||||
srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
@@ -411,143 +427,21 @@ void ImGui_ImplDX12_UpdateTexture(ImTextureData* tex)
|
||||
srvDesc.Texture2D.MipLevels = desc.MipLevels;
|
||||
srvDesc.Texture2D.MostDetailedMip = 0;
|
||||
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||
bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, backend_tex->hFontSrvCpuDescHandle);
|
||||
SafeRelease(backend_tex->pTextureResource);
|
||||
backend_tex->pTextureResource = pTexture;
|
||||
|
||||
// Store identifiers
|
||||
tex->SetTexID((ImTextureID)backend_tex->hFontSrvGpuDescHandle.ptr);
|
||||
tex->BackendUserData = backend_tex;
|
||||
need_barrier_before_copy = false; // Because this is a newly-created texture it will be in D3D12_RESOURCE_STATE_COMMON and thus we don't need a barrier
|
||||
// We don't set tex->Status to ImTextureStatus_OK to let the code fallthrough below.
|
||||
bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, bd->hFontSrvCpuDescHandle);
|
||||
SafeRelease(bd->pFontTextureResource);
|
||||
bd->pFontTextureResource = pTexture;
|
||||
}
|
||||
|
||||
if (tex->Status == ImTextureStatus_WantCreate || tex->Status == ImTextureStatus_WantUpdates)
|
||||
{
|
||||
ImGui_ImplDX12_Texture* backend_tex = (ImGui_ImplDX12_Texture*)tex->BackendUserData;
|
||||
IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
|
||||
|
||||
// We could use the smaller rect on _WantCreate but using the full rect allows us to clear the texture.
|
||||
// FIXME-OPT: Uploading single box even when using ImTextureStatus_WantUpdates. Could use tex->Updates[]
|
||||
// - Copy all blocks contiguously in upload buffer.
|
||||
// - Barrier before copy, submit all CopyTextureRegion(), barrier after copy.
|
||||
const int upload_x = (tex->Status == ImTextureStatus_WantCreate) ? 0 : tex->UpdateRect.x;
|
||||
const int upload_y = (tex->Status == ImTextureStatus_WantCreate) ? 0 : tex->UpdateRect.y;
|
||||
const int upload_w = (tex->Status == ImTextureStatus_WantCreate) ? tex->Width : tex->UpdateRect.w;
|
||||
const int upload_h = (tex->Status == ImTextureStatus_WantCreate) ? tex->Height : tex->UpdateRect.h;
|
||||
|
||||
// Update full texture or selected blocks. We only ever write to textures regions which have never been used before!
|
||||
// This backend choose to use tex->UpdateRect but you can use tex->Updates[] to upload individual regions.
|
||||
UINT upload_pitch_src = upload_w * tex->BytesPerPixel;
|
||||
UINT upload_pitch_dst = (upload_pitch_src + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u);
|
||||
UINT upload_size = upload_pitch_dst * upload_h;
|
||||
|
||||
if (bd->pTexUploadBuffer == nullptr || upload_size > bd->pTexUploadBufferSize)
|
||||
{
|
||||
if (bd->pTexUploadBufferMapped)
|
||||
{
|
||||
D3D12_RANGE range = { 0, bd->pTexUploadBufferSize };
|
||||
bd->pTexUploadBuffer->Unmap(0, &range);
|
||||
bd->pTexUploadBufferMapped = nullptr;
|
||||
}
|
||||
SafeRelease(bd->pTexUploadBuffer);
|
||||
|
||||
D3D12_RESOURCE_DESC desc;
|
||||
ZeroMemory(&desc, sizeof(desc));
|
||||
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
|
||||
desc.Alignment = 0;
|
||||
desc.Width = upload_size;
|
||||
desc.Height = 1;
|
||||
desc.DepthOrArraySize = 1;
|
||||
desc.MipLevels = 1;
|
||||
desc.Format = DXGI_FORMAT_UNKNOWN;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.SampleDesc.Quality = 0;
|
||||
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
||||
|
||||
D3D12_HEAP_PROPERTIES props;
|
||||
memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES));
|
||||
props.Type = D3D12_HEAP_TYPE_UPLOAD;
|
||||
props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||
props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||
|
||||
HRESULT hr = bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc,
|
||||
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&bd->pTexUploadBuffer));
|
||||
IM_ASSERT(SUCCEEDED(hr));
|
||||
|
||||
D3D12_RANGE range = {0, upload_size};
|
||||
hr = bd->pTexUploadBuffer->Map(0, &range, &bd->pTexUploadBufferMapped);
|
||||
IM_ASSERT(SUCCEEDED(hr));
|
||||
bd->pTexUploadBufferSize = upload_size;
|
||||
}
|
||||
|
||||
bd->pTexCmdAllocator->Reset();
|
||||
bd->pTexCmdList->Reset(bd->pTexCmdAllocator, nullptr);
|
||||
ID3D12GraphicsCommandList* cmdList = bd->pTexCmdList;
|
||||
|
||||
// Copy to upload buffer
|
||||
for (int y = 0; y < upload_h; y++)
|
||||
memcpy((void*)((uintptr_t)bd->pTexUploadBufferMapped + y * upload_pitch_dst), tex->GetPixelsAt(upload_x, upload_y + y), upload_pitch_src);
|
||||
|
||||
if (need_barrier_before_copy)
|
||||
{
|
||||
D3D12_RESOURCE_BARRIER barrier = {};
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = backend_tex->pTextureResource;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
cmdList->ResourceBarrier(1, &barrier);
|
||||
}
|
||||
|
||||
D3D12_TEXTURE_COPY_LOCATION srcLocation = {};
|
||||
D3D12_TEXTURE_COPY_LOCATION dstLocation = {};
|
||||
{
|
||||
srcLocation.pResource = bd->pTexUploadBuffer;
|
||||
srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
||||
srcLocation.PlacedFootprint.Footprint.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
srcLocation.PlacedFootprint.Footprint.Width = upload_w;
|
||||
srcLocation.PlacedFootprint.Footprint.Height = upload_h;
|
||||
srcLocation.PlacedFootprint.Footprint.Depth = 1;
|
||||
srcLocation.PlacedFootprint.Footprint.RowPitch = upload_pitch_dst;
|
||||
dstLocation.pResource = backend_tex->pTextureResource;
|
||||
dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
||||
dstLocation.SubresourceIndex = 0;
|
||||
}
|
||||
cmdList->CopyTextureRegion(&dstLocation, upload_x, upload_y, 0, &srcLocation, nullptr);
|
||||
|
||||
{
|
||||
D3D12_RESOURCE_BARRIER barrier = {};
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
|
||||
barrier.Transition.pResource = backend_tex->pTextureResource;
|
||||
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
||||
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
|
||||
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
|
||||
cmdList->ResourceBarrier(1, &barrier);
|
||||
}
|
||||
|
||||
HRESULT hr = cmdList->Close();
|
||||
IM_ASSERT(SUCCEEDED(hr));
|
||||
ID3D12CommandQueue* cmdQueue = bd->pCommandQueue;
|
||||
cmdQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmdList);
|
||||
hr = cmdQueue->Signal(bd->Fence, ++bd->FenceLastSignaledValue);
|
||||
IM_ASSERT(SUCCEEDED(hr));
|
||||
|
||||
// FIXME-OPT: Suboptimal?
|
||||
// - To remove this may need to create NumFramesInFlight x ImGui_ImplDX12_FrameContext in backend data (mimick docking version)
|
||||
// - Store per-frame in flight: upload buffer?
|
||||
// - Where do cmdList and cmdAlloc fit?
|
||||
bd->Fence->SetEventOnCompletion(bd->FenceLastSignaledValue, bd->FenceEvent);
|
||||
::WaitForSingleObject(bd->FenceEvent, INFINITE);
|
||||
|
||||
tex->SetStatus(ImTextureStatus_OK);
|
||||
}
|
||||
|
||||
if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames >= (int)bd->numFramesInFlight)
|
||||
ImGui_ImplDX12_DestroyTexture(tex);
|
||||
// Store our identifier
|
||||
// READ THIS IF THE STATIC_ASSERT() TRIGGERS:
|
||||
// - Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'.
|
||||
// - This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*.
|
||||
// [Solution 1] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'ImTextureID=ImU64' (this is what we do in the 'example_win32_direct12/example_win32_direct12.vcxproj' project file)
|
||||
// [Solution 2] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'IMGUI_USER_CONFIG="my_imgui_config.h"' and inside 'my_imgui_config.h' add '#define ImTextureID ImU64' and as many other options as you like.
|
||||
// [Solution 3] IDE/msbuild: edit imconfig.h and add '#define ImTextureID ImU64' (prefer solution 2 to create your own config file!)
|
||||
// [Solution 4] command-line: add '/D ImTextureID=ImU64' to your cl.exe command-line (this is what we do in the example_win32_direct12/build_win32.bat file)
|
||||
static_assert(sizeof(ImTextureID) >= sizeof(bd->hFontSrvGpuDescHandle.ptr), "Can't pack descriptor handle into TexID, 32-bit not supported yet.");
|
||||
io.Fonts->SetTexID((ImTextureID)bd->hFontSrvGpuDescHandle.ptr);
|
||||
}
|
||||
|
||||
bool ImGui_ImplDX12_CreateDeviceObjects()
|
||||
@@ -558,13 +452,6 @@ bool ImGui_ImplDX12_CreateDeviceObjects()
|
||||
if (bd->pPipelineState)
|
||||
ImGui_ImplDX12_InvalidateDeviceObjects();
|
||||
|
||||
HRESULT hr = ::CreateDXGIFactory1(IID_PPV_ARGS(&bd->pdxgiFactory));
|
||||
IM_ASSERT(hr == S_OK);
|
||||
|
||||
BOOL allow_tearing = FALSE;
|
||||
bd->pdxgiFactory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing, sizeof(allow_tearing));
|
||||
bd->tearingSupport = (allow_tearing == TRUE);
|
||||
|
||||
// Create the root signature
|
||||
{
|
||||
D3D12_DESCRIPTOR_RANGE descRange = {};
|
||||
@@ -588,26 +475,26 @@ bool ImGui_ImplDX12_CreateDeviceObjects()
|
||||
param[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
|
||||
|
||||
// Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling.
|
||||
D3D12_STATIC_SAMPLER_DESC staticSampler[1] = {};
|
||||
staticSampler[0].Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
staticSampler[0].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSampler[0].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSampler[0].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
|
||||
staticSampler[0].MipLODBias = 0.f;
|
||||
staticSampler[0].MaxAnisotropy = 0;
|
||||
staticSampler[0].ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS;
|
||||
staticSampler[0].BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
|
||||
staticSampler[0].MinLOD = 0.f;
|
||||
staticSampler[0].MaxLOD = D3D12_FLOAT32_MAX;
|
||||
staticSampler[0].ShaderRegister = 0;
|
||||
staticSampler[0].RegisterSpace = 0;
|
||||
staticSampler[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
|
||||
D3D12_STATIC_SAMPLER_DESC staticSampler = {};
|
||||
staticSampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
|
||||
staticSampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
staticSampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
staticSampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
|
||||
staticSampler.MipLODBias = 0.f;
|
||||
staticSampler.MaxAnisotropy = 0;
|
||||
staticSampler.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS;
|
||||
staticSampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
|
||||
staticSampler.MinLOD = 0.f;
|
||||
staticSampler.MaxLOD = 0.f;
|
||||
staticSampler.ShaderRegister = 0;
|
||||
staticSampler.RegisterSpace = 0;
|
||||
staticSampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
|
||||
|
||||
D3D12_ROOT_SIGNATURE_DESC desc = {};
|
||||
desc.NumParameters = _countof(param);
|
||||
desc.pParameters = param;
|
||||
desc.NumStaticSamplers = 1;
|
||||
desc.pStaticSamplers = &staticSampler[0];
|
||||
desc.pStaticSamplers = &staticSampler;
|
||||
desc.Flags =
|
||||
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
|
||||
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS |
|
||||
@@ -636,7 +523,7 @@ bool ImGui_ImplDX12_CreateDeviceObjects()
|
||||
return false;
|
||||
}
|
||||
|
||||
_PFN_D3D12_SERIALIZE_ROOT_SIGNATURE D3D12SerializeRootSignatureFn = (_PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)(void*)::GetProcAddress(d3d12_dll, "D3D12SerializeRootSignature");
|
||||
PFN_D3D12_SERIALIZE_ROOT_SIGNATURE D3D12SerializeRootSignatureFn = (PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)::GetProcAddress(d3d12_dll, "D3D12SerializeRootSignature");
|
||||
if (D3D12SerializeRootSignatureFn == nullptr)
|
||||
return false;
|
||||
|
||||
@@ -654,14 +541,14 @@ bool ImGui_ImplDX12_CreateDeviceObjects()
|
||||
// 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL.
|
||||
// See https://github.com/ocornut/imgui/pull/638 for sources and details.
|
||||
|
||||
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
|
||||
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc;
|
||||
memset(&psoDesc, 0, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
|
||||
psoDesc.NodeMask = 1;
|
||||
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
|
||||
psoDesc.pRootSignature = bd->pRootSignature;
|
||||
psoDesc.SampleMask = UINT_MAX;
|
||||
psoDesc.NumRenderTargets = 1;
|
||||
psoDesc.RTVFormats[0] = bd->RTVFormat;
|
||||
psoDesc.DSVFormat = bd->DSVFormat;
|
||||
psoDesc.SampleDesc.Count = 1;
|
||||
psoDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
|
||||
|
||||
@@ -786,19 +673,7 @@ bool ImGui_ImplDX12_CreateDeviceObjects()
|
||||
if (result_pipeline_state != S_OK)
|
||||
return false;
|
||||
|
||||
// Create command allocator and command list for ImGui_ImplDX12_UpdateTexture()
|
||||
hr = bd->pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&bd->pTexCmdAllocator));
|
||||
IM_ASSERT(SUCCEEDED(hr));
|
||||
hr = bd->pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, bd->pTexCmdAllocator, nullptr, IID_PPV_ARGS(&bd->pTexCmdList));
|
||||
IM_ASSERT(SUCCEEDED(hr));
|
||||
hr = bd->pTexCmdList->Close();
|
||||
IM_ASSERT(SUCCEEDED(hr));
|
||||
|
||||
// Create fence.
|
||||
hr = bd->pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&bd->Fence));
|
||||
IM_ASSERT(hr == S_OK);
|
||||
bd->FenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
IM_ASSERT(bd->FenceEvent != nullptr);
|
||||
ImGui_ImplDX12_CreateFontsTexture();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -809,29 +684,11 @@ void ImGui_ImplDX12_InvalidateDeviceObjects()
|
||||
if (!bd || !bd->pd3dDevice)
|
||||
return;
|
||||
|
||||
SafeRelease(bd->pdxgiFactory);
|
||||
if (bd->commandQueueOwned)
|
||||
SafeRelease(bd->pCommandQueue);
|
||||
bd->commandQueueOwned = false;
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
SafeRelease(bd->pRootSignature);
|
||||
SafeRelease(bd->pPipelineState);
|
||||
if (bd->pTexUploadBufferMapped)
|
||||
{
|
||||
D3D12_RANGE range = { 0, bd->pTexUploadBufferSize };
|
||||
bd->pTexUploadBuffer->Unmap(0, &range);
|
||||
bd->pTexUploadBufferMapped = nullptr;
|
||||
}
|
||||
SafeRelease(bd->pTexUploadBuffer);
|
||||
SafeRelease(bd->pTexCmdList);
|
||||
SafeRelease(bd->pTexCmdAllocator);
|
||||
SafeRelease(bd->Fence);
|
||||
CloseHandle(bd->FenceEvent);
|
||||
bd->FenceEvent = nullptr;
|
||||
|
||||
// Destroy all textures
|
||||
for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)
|
||||
if (tex->RefCount == 1)
|
||||
ImGui_ImplDX12_DestroyTexture(tex);
|
||||
SafeRelease(bd->pFontTextureResource);
|
||||
io.Fonts->SetTexID(0); // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well.
|
||||
|
||||
for (UINT i = 0; i < bd->numFramesInFlight; i++)
|
||||
{
|
||||
@@ -841,29 +698,8 @@ void ImGui_ImplDX12_InvalidateDeviceObjects()
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
static void ImGui_ImplDX12_InitLegacySingleDescriptorMode(ImGui_ImplDX12_InitInfo* init_info)
|
||||
{
|
||||
// Wrap legacy behavior of passing space for a single descriptor
|
||||
IM_ASSERT(init_info->LegacySingleSrvCpuDescriptor.ptr != 0 && init_info->LegacySingleSrvGpuDescriptor.ptr != 0);
|
||||
init_info->SrvDescriptorAllocFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE* out_cpu_handle, D3D12_GPU_DESCRIPTOR_HANDLE* out_gpu_handle)
|
||||
{
|
||||
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
||||
IM_ASSERT(bd->LegacySingleDescriptorUsed == false && "Only 1 simultaneous texture allowed with legacy ImGui_ImplDX12_Init() signature!");
|
||||
*out_cpu_handle = bd->InitInfo.LegacySingleSrvCpuDescriptor;
|
||||
*out_gpu_handle = bd->InitInfo.LegacySingleSrvGpuDescriptor;
|
||||
bd->LegacySingleDescriptorUsed = true;
|
||||
};
|
||||
init_info->SrvDescriptorFreeFn = [](ImGui_ImplDX12_InitInfo*, D3D12_CPU_DESCRIPTOR_HANDLE, D3D12_GPU_DESCRIPTOR_HANDLE)
|
||||
{
|
||||
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
||||
IM_ASSERT(bd->LegacySingleDescriptorUsed == true);
|
||||
bd->LegacySingleDescriptorUsed = false;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info)
|
||||
bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* cbv_srv_heap,
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
IMGUI_CHECKVERSION();
|
||||
@@ -871,33 +707,21 @@ bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info)
|
||||
|
||||
// Setup backend capabilities flags
|
||||
ImGui_ImplDX12_Data* bd = IM_NEW(ImGui_ImplDX12_Data)();
|
||||
bd->InitInfo = *init_info; // Deep copy
|
||||
init_info = &bd->InitInfo;
|
||||
|
||||
bd->pd3dDevice = init_info->Device;
|
||||
IM_ASSERT(init_info->CommandQueue != NULL);
|
||||
bd->pCommandQueue = init_info->CommandQueue;
|
||||
bd->RTVFormat = init_info->RTVFormat;
|
||||
bd->DSVFormat = init_info->DSVFormat;
|
||||
bd->numFramesInFlight = init_info->NumFramesInFlight;
|
||||
bd->pd3dSrvDescHeap = init_info->SrvDescriptorHeap;
|
||||
bd->tearingSupport = false;
|
||||
|
||||
io.BackendRendererUserData = (void*)bd;
|
||||
io.BackendRendererName = "imgui_impl_dx12";
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
||||
io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render.
|
||||
|
||||
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
if (init_info->SrvDescriptorAllocFn == nullptr)
|
||||
ImGui_ImplDX12_InitLegacySingleDescriptorMode(init_info);
|
||||
#endif
|
||||
IM_ASSERT(init_info->SrvDescriptorAllocFn != nullptr && init_info->SrvDescriptorFreeFn != nullptr);
|
||||
bd->pd3dDevice = device;
|
||||
bd->RTVFormat = rtv_format;
|
||||
bd->hFontSrvCpuDescHandle = font_srv_cpu_desc_handle;
|
||||
bd->hFontSrvGpuDescHandle = font_srv_gpu_desc_handle;
|
||||
bd->pFrameResources = new ImGui_ImplDX12_RenderBuffers[num_frames_in_flight];
|
||||
bd->numFramesInFlight = num_frames_in_flight;
|
||||
bd->pd3dSrvDescHeap = cbv_srv_heap;
|
||||
bd->frameIndex = UINT_MAX;
|
||||
|
||||
// Create buffers with a default size (they will later be grown as needed)
|
||||
bd->frameIndex = UINT_MAX;
|
||||
bd->pFrameResources = new ImGui_ImplDX12_RenderBuffers[bd->numFramesInFlight];
|
||||
for (int i = 0; i < (int)bd->numFramesInFlight; i++)
|
||||
for (int i = 0; i < num_frames_in_flight; i++)
|
||||
{
|
||||
ImGui_ImplDX12_RenderBuffers* fr = &bd->pFrameResources[i];
|
||||
fr->IndexBuffer = nullptr;
|
||||
@@ -909,50 +733,18 @@ bool ImGui_ImplDX12_Init(ImGui_ImplDX12_InitInfo* init_info)
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
// Legacy initialization API Obsoleted in 1.91.5
|
||||
// font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture, they must be in 'srv_descriptor_heap'
|
||||
bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* srv_descriptor_heap, D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle)
|
||||
{
|
||||
ImGui_ImplDX12_InitInfo init_info;
|
||||
init_info.Device = device;
|
||||
init_info.NumFramesInFlight = num_frames_in_flight;
|
||||
init_info.RTVFormat = rtv_format;
|
||||
init_info.SrvDescriptorHeap = srv_descriptor_heap;
|
||||
init_info.LegacySingleSrvCpuDescriptor = font_srv_cpu_desc_handle;
|
||||
init_info.LegacySingleSrvGpuDescriptor = font_srv_gpu_desc_handle;
|
||||
|
||||
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
|
||||
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
||||
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
||||
queueDesc.NodeMask = 1;
|
||||
HRESULT hr = device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&init_info.CommandQueue));
|
||||
IM_ASSERT(SUCCEEDED(hr));
|
||||
|
||||
bool ret = ImGui_ImplDX12_Init(&init_info);
|
||||
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
||||
bd->commandQueueOwned = true;
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.BackendFlags &= ~ImGuiBackendFlags_RendererHasTextures; // Using legacy ImGui_ImplDX12_Init() call with 1 SRV descriptor we cannot support multiple textures.
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
void ImGui_ImplDX12_Shutdown()
|
||||
{
|
||||
ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData();
|
||||
IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
|
||||
|
||||
// Clean up windows and device objects
|
||||
ImGui_ImplDX12_InvalidateDeviceObjects();
|
||||
delete[] bd->pFrameResources;
|
||||
|
||||
io.BackendRendererName = nullptr;
|
||||
io.BackendRendererUserData = nullptr;
|
||||
io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures);
|
||||
platform_io.ClearRendererHandlers();
|
||||
io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset;
|
||||
IM_DELETE(bd);
|
||||
}
|
||||
|
||||
@@ -962,8 +754,7 @@ void ImGui_ImplDX12_NewFrame()
|
||||
IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplDX12_Init()?");
|
||||
|
||||
if (!bd->pPipelineState)
|
||||
if (!ImGui_ImplDX12_CreateDeviceObjects())
|
||||
IM_ASSERT(0 && "ImGui_ImplDX12_CreateDeviceObjects() failed!");
|
||||
ImGui_ImplDX12_CreateDeviceObjects();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user