mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 04:08:32 +09:00
Compare commits
40
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4bf9c1cae6 | ||
|
|
c121328c98 | ||
|
|
5fe5aae138 | ||
|
|
d2485a3fbe | ||
|
|
3fe0999550 | ||
|
|
39eb6a9026 | ||
|
|
99c1697fba | ||
|
|
5db5545b33 | ||
|
|
e63eebf425 | ||
|
|
03d3fcdc66 | ||
|
|
40fc317f69 | ||
|
|
3bafdee45d | ||
|
|
188275c004 | ||
|
|
cd3ff1f881 | ||
|
|
6c10876532 | ||
|
|
ce8a2356d7 | ||
|
|
37f26d1bb1 | ||
|
|
22bc4fbbc9 | ||
|
|
18941f9e83 | ||
|
|
0b332bdc58 | ||
|
|
2abdebcd96 | ||
|
|
e39af3b940 | ||
|
|
3b2c2028b9 | ||
|
|
55020a1e86 | ||
|
|
6d4ddc47f6 | ||
|
|
2aff5dc650 | ||
|
|
7aa7b82628 | ||
|
|
fd530156a6 | ||
|
|
56c98041e3 | ||
|
|
6eeb67f08b | ||
|
|
9a6cf7fc7f | ||
|
|
938b92277f | ||
|
|
419ffb3b5e | ||
|
|
ad7d145ece | ||
|
|
516566321d | ||
|
|
7918300848 | ||
|
|
00679642d6 | ||
|
|
4769a9cf7b | ||
|
|
9f9e955b92 | ||
|
|
85f7c8f4a6 |
@@ -5,6 +5,7 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- dev
|
- dev
|
||||||
- Feat/Backend-Direct-GLES
|
- Feat/Backend-Direct-GLES
|
||||||
|
- Feat/Backend-Direct-Vulkan
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
benchmark:
|
benchmark:
|
||||||
@@ -34,7 +35,7 @@ jobs:
|
|||||||
- name: Install clang-20
|
- name: Install clang-20
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y clang-20 clang++-20 lld-20 libc++-20-dev libc++abi-20-dev
|
sudo apt-get install -y clang-20 clang++-20 lld-20 libc++-20-dev libc++abi-20-dev libvulkan-dev
|
||||||
|
|
||||||
- name: Show installed toolchain
|
- name: Show installed toolchain
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- dev
|
- dev
|
||||||
- Feat/Backend-Direct-GLES
|
- Feat/Backend-Direct-GLES
|
||||||
|
- Feat/Backend-Direct-Vulkan
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
@@ -34,7 +35,7 @@ jobs:
|
|||||||
- name: Install clang-20
|
- name: Install clang-20
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y clang-20 clang++-20 lld-20 libc++-20-dev libc++abi-20-dev
|
sudo apt-get install -y clang-20 clang++-20 lld-20 libc++-20-dev libc++abi-20-dev libvulkan-dev
|
||||||
|
|
||||||
- name: Show installed toolchain
|
- name: Show installed toolchain
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -14,3 +14,4 @@ MobileGLCodeManager
|
|||||||
.vscode
|
.vscode
|
||||||
.clangd
|
.clangd
|
||||||
MobileGL/MG_Test/build
|
MobileGL/MG_Test/build
|
||||||
|
/build_*
|
||||||
@@ -13,3 +13,6 @@
|
|||||||
[submodule "3rdparty/tracy"]
|
[submodule "3rdparty/tracy"]
|
||||||
path = 3rdparty/tracy
|
path = 3rdparty/tracy
|
||||||
url = https://github.com/wolfpld/tracy.git
|
url = https://github.com/wolfpld/tracy.git
|
||||||
|
[submodule "3rdparty/xxHash"]
|
||||||
|
path = 3rdparty/xxHash
|
||||||
|
url = https://github.com/Cyan4973/xxHash.git
|
||||||
|
|||||||
+1
Submodule 3rdparty/xxHash added at 1d7b2a9d21
+31
-5
@@ -108,6 +108,10 @@ set(SPIRV_CROSS_STATIC ON CACHE BOOL "Prefer static libs" FORCE)
|
|||||||
add_subdirectory(3rdparty/glslang)
|
add_subdirectory(3rdparty/glslang)
|
||||||
add_subdirectory(3rdparty/SPIRV-Cross)
|
add_subdirectory(3rdparty/SPIRV-Cross)
|
||||||
|
|
||||||
|
set(XXHASH_BUILD_XXHSUM OFF)
|
||||||
|
option(BUILD_SHARED_LIBS OFF)
|
||||||
|
add_subdirectory(3rdparty/xxHash/build/cmake xxhash_build EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
set(TRACY_ENABLE ${MOBILEGL_ENABLE_TRACY} CACHE BOOL "Enable Tracy, this is an internal variable" FORCE)
|
set(TRACY_ENABLE ${MOBILEGL_ENABLE_TRACY} CACHE BOOL "Enable Tracy, this is an internal variable" FORCE)
|
||||||
|
|
||||||
if (TRACY_ENABLE)
|
if (TRACY_ENABLE)
|
||||||
@@ -164,6 +168,7 @@ set(SOURCE_FILES
|
|||||||
MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/EliminateFloatEqualsZeroPass.cpp
|
MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/EliminateFloatEqualsZeroPass.cpp
|
||||||
|
|
||||||
MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp
|
MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp
|
||||||
|
MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp
|
||||||
|
|
||||||
MobileGL/MG_Util/Texture/PixelStoreProcessor.cpp
|
MobileGL/MG_Util/Texture/PixelStoreProcessor.cpp
|
||||||
MobileGL/MG_Util/Texture/TextureFormatProcessor.cpp
|
MobileGL/MG_Util/Texture/TextureFormatProcessor.cpp
|
||||||
@@ -204,6 +209,15 @@ set(SOURCE_FILES
|
|||||||
MobileGL/MG_Backend/DirectGLES/Utils.cpp
|
MobileGL/MG_Backend/DirectGLES/Utils.cpp
|
||||||
MobileGL/MG_Backend/DirectGLES/Managers.cpp
|
MobileGL/MG_Backend/DirectGLES/Managers.cpp
|
||||||
|
|
||||||
|
MobileGL/MG_Backend/DirectVulkanTMP/DirectVulkanTMP.cpp
|
||||||
|
MobileGL/MG_Backend/DirectVulkanTMP/TmpImpl.cpp
|
||||||
|
MobileGL/MG_Backend/DirectVulkanTMP/BackendObject_DirectVulkanTMP.cpp
|
||||||
|
MobileGL/MG_Backend/DirectVulkanTMP/Renderer/VulkanContext.cpp
|
||||||
|
MobileGL/MG_Backend/DirectVulkanTMP/Renderer/SwapchainManager.cpp
|
||||||
|
MobileGL/MG_Backend/DirectVulkanTMP/Renderer/PipelineManager.cpp
|
||||||
|
MobileGL/MG_Backend/DirectVulkanTMP/Renderer/FrameContext.cpp
|
||||||
|
MobileGL/MG_Backend/DirectVulkanTMP/Managers/ProgramManager.cpp
|
||||||
|
|
||||||
MobileGL/MG_State/GLState/Core.cpp
|
MobileGL/MG_State/GLState/Core.cpp
|
||||||
MobileGL/MG_State/GLState/ErrorState/Error.cpp
|
MobileGL/MG_State/GLState/ErrorState/Error.cpp
|
||||||
MobileGL/MG_State/GLState/BufferState/BufferState.cpp
|
MobileGL/MG_State/GLState/BufferState/BufferState.cpp
|
||||||
@@ -236,6 +250,7 @@ set(MOBILEGL_LINK_LIBRARIES
|
|||||||
spirv-cross-c
|
spirv-cross-c
|
||||||
SPIRV-Tools-opt
|
SPIRV-Tools-opt
|
||||||
SPIRV-Tools
|
SPIRV-Tools
|
||||||
|
xxHash::xxhash
|
||||||
)
|
)
|
||||||
|
|
||||||
set(MOBILEGL_INCLUDE_DIR
|
set(MOBILEGL_INCLUDE_DIR
|
||||||
@@ -314,13 +329,24 @@ if (ANDROID)
|
|||||||
target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC
|
target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC
|
||||||
android
|
android
|
||||||
log
|
log
|
||||||
|
vulkan
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (MOBILEGL_BUILD_TEST)
|
if (NOT ANDROID)
|
||||||
add_subdirectory(MobileGL/MG_Test)
|
find_package(Vulkan)
|
||||||
endif()
|
if (Vulkan_FOUND)
|
||||||
|
target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC Vulkan::Vulkan Vulkan::Headers)
|
||||||
|
target_link_libraries(${CMAKE_PROJECT_NAME}_s PUBLIC Vulkan::Vulkan Vulkan::Headers)
|
||||||
|
target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC ${Vulkan_INCLUDE_DIR})
|
||||||
|
target_include_directories(${CMAKE_PROJECT_NAME}_s PUBLIC ${Vulkan_INCLUDE_DIR})
|
||||||
|
endif ()
|
||||||
|
|
||||||
if (MOBILEGL_BUILD_BENCHMARK)
|
if (MOBILEGL_BUILD_TEST)
|
||||||
add_subdirectory(MobileGL/MG_Benchmark)
|
add_subdirectory(MobileGL/MG_Test)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (MOBILEGL_BUILD_BENCHMARK)
|
||||||
|
add_subdirectory(MobileGL/MG_Benchmark)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
namespace MobileGL {
|
namespace MobileGL {
|
||||||
namespace MG_Config {
|
namespace MG_Config {
|
||||||
BackendType ActiveBackendType;
|
BackendType ActiveBackendType = BackendType::DirectVulkanTMP;
|
||||||
} // namespace MG_Config
|
} // namespace MG_Config
|
||||||
|
|
||||||
namespace MG_Backend {
|
namespace MG_Backend {
|
||||||
|
|||||||
+6
-4
@@ -17,9 +17,11 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <mutex>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <bitset>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@@ -29,19 +31,19 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <cassert>
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <expected>
|
#include <expected>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <xxhash.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <mutex>
|
|
||||||
#include <bitset>
|
|
||||||
#if __cplusplus >= 202302L && MOBILEGL_LOG_ENABLE_STACKTRACE
|
#if __cplusplus >= 202302L && MOBILEGL_LOG_ENABLE_STACKTRACE
|
||||||
#include <stacktrace>
|
#include <stacktrace>
|
||||||
#endif
|
#endif
|
||||||
@@ -90,13 +92,13 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
|
#define VK_USE_PLATFORM_ANDROID_KHR
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
#include <vulkan/vulkan.h>
|
|
||||||
#include <android/native_window.h>
|
#include <android/native_window.h>
|
||||||
#include <vulkan/vulkan_android.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
#ifdef TRACY_ENABLE
|
#ifdef TRACY_ENABLE
|
||||||
#include <tracy/Tracy.hpp>
|
#include <tracy/Tracy.hpp>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
namespace MobileGL {
|
namespace MobileGL {
|
||||||
enum class BackendType {
|
enum class BackendType {
|
||||||
DirectGLES,
|
DirectGLES,
|
||||||
|
DirectVulkanTMP,
|
||||||
BackendTypeCount,
|
BackendTypeCount,
|
||||||
Unknown = -1
|
Unknown = -1
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <Includes.h>
|
#include <Includes.h>
|
||||||
#include "BackendObject.h"
|
#include "BackendObject.h"
|
||||||
#include "DirectGLES/BackendObject_DirectGLES.h"
|
#include "DirectGLES/BackendObject_DirectGLES.h"
|
||||||
|
#include "DirectVulkanTMP/BackendObject_DirectVulkanTMP.h"
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend {
|
namespace MobileGL::MG_Backend {
|
||||||
extern UniquePtr<BackendObject> pActiveBackendObject;
|
extern UniquePtr<BackendObject> pActiveBackendObject;
|
||||||
|
|||||||
@@ -0,0 +1,119 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Backend/DirectVulkanTMP/BackendObject_DirectVulkanTMP.cpp
|
||||||
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||||
|
// Licensed under the GNU Lesser General Public License v3.0:
|
||||||
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only
|
||||||
|
// End of Source File Header
|
||||||
|
|
||||||
|
#include "BackendObject_DirectVulkanTMP.h"
|
||||||
|
#include "MG_Backend/BackendObject.h"
|
||||||
|
#include "DirectVulkanTMP.h"
|
||||||
|
#include "TmpImpl.h"
|
||||||
|
#include <MG_Util/BackendLoaders/OpenGL/Loader.h>
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Backend::DirectVulkanTMP {
|
||||||
|
BackendObject_DirectVulkanTMP::~BackendObject_DirectVulkanTMP() = default;
|
||||||
|
|
||||||
|
void BackendObject_DirectVulkanTMP::InitWindowSurface() {
|
||||||
|
auto nativeWindow = reinterpret_cast<NativeWindowType>(m_windowHandle.Handle);
|
||||||
|
if (!DirectVulkanTMP::InitWindowSurface(nativeWindow)) {
|
||||||
|
MGLOG_E("Failed to initialize window surface for DirectVulkanTMP backend");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackendObject_DirectVulkanTMP::Initialize() {
|
||||||
|
m_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackendObject_DirectVulkanTMP::InitCapabilities() {
|
||||||
|
if (!m_initialized) {
|
||||||
|
MGLOG_E("Cannot initialize capabilities before backend is initialized");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MG_Util::BackendLoader::QueryVulkanCapabilities(m_vulkanCaps,
|
||||||
|
DirectVulkanTMP::GetVulkanState().ctx->GetPhysicalDevice());
|
||||||
|
UpdateDynamicBackendParameters();
|
||||||
|
}
|
||||||
|
|
||||||
|
const RendererInfo& BackendObject_DirectVulkanTMP::GetRendererInfo() const {
|
||||||
|
static RendererInfo RendererInfo = {
|
||||||
|
.RendererName = "Magma-TMP", // Renderer Name
|
||||||
|
.BackendName = "Direct (Vulkan) TMP", // Backend Name
|
||||||
|
.ExtraVendor = Nullopt, // Extra vendor
|
||||||
|
.RendererGLInfo =
|
||||||
|
{
|
||||||
|
.TargetGLVersion = {3, 3, 0}, // Target OpenGL Version
|
||||||
|
.TargetGLSLVersion = {4, 6, 0}, // Target Shading Language Version
|
||||||
|
.Extensions = {V_OpenGL30, V_OpenGL31, V_OpenGL32, // OpenGL Extensions
|
||||||
|
V_OpenGL33},
|
||||||
|
.IsCompatibilityProfile = false // Is Compatibility Profile
|
||||||
|
},
|
||||||
|
.StaticBackendCapability = {.AllowVSOnlyPrograms = false} // Backend Capability
|
||||||
|
};
|
||||||
|
return RendererInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
String BackendObject_DirectVulkanTMP::GetBackendAPIVersionString() const {
|
||||||
|
if (!m_initialized) {
|
||||||
|
return "<uninitialized DirectVulkanTMP backend>";
|
||||||
|
}
|
||||||
|
// Format:
|
||||||
|
// <GPU Name>, Vulkan <Vulkan Version>, Driver <Driver Version>
|
||||||
|
// TODO
|
||||||
|
String str = m_vulkanCaps.DeviceName + ", Vulkan " + m_vulkanCaps.VulkanAPIVersion.toString() + ", Driver " +
|
||||||
|
m_vulkanCaps.DriverVersionString;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
BackendType BackendObject_DirectVulkanTMP::GetBackendType() const {
|
||||||
|
return BackendType::DirectVulkanTMP;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GlobalBackendFunctionsTable& BackendObject_DirectVulkanTMP::GetBackendFunctions() const {
|
||||||
|
static GlobalBackendFunctionsTable funcsTable;
|
||||||
|
static Bool funcsTableInitialized = false;
|
||||||
|
if (!funcsTableInitialized) {
|
||||||
|
funcsTable.Present = DirectVulkanTMP::Present;
|
||||||
|
funcsTable.GL.DrawArrays = DrawArrays;
|
||||||
|
funcsTable.GL.DrawElements = DrawElements;
|
||||||
|
funcsTable.GL.DrawElementsBaseVertex = DrawElementsBaseVertex;
|
||||||
|
funcsTable.GL.MultiDrawElements = MultiDrawElements;
|
||||||
|
funcsTable.GL.MultiDrawElementsBaseVertex = MultiDrawElementsBaseVertex;
|
||||||
|
funcsTable.GL.MultiDrawElementsIndirect = MultiDrawElementsIndirect;
|
||||||
|
funcsTable.GL.MultiDrawArraysIndirect = MultiDrawArraysIndirect;
|
||||||
|
funcsTable.GL.DrawRangeElementsBaseVertex = DrawRangeElementsBaseVertex;
|
||||||
|
funcsTable.GL.DrawRangeElements = DrawRangeElements;
|
||||||
|
funcsTable.GL.DrawElementsInstancedBaseVertexBaseInstance = DrawElementsInstancedBaseVertexBaseInstance;
|
||||||
|
funcsTable.GL.DrawElementsInstancedBaseVertex = DrawElementsInstancedBaseVertex;
|
||||||
|
funcsTable.GL.DrawElementsInstancedBaseInstance = DrawElementsInstancedBaseInstance;
|
||||||
|
funcsTable.GL.DrawElementsInstanced = DrawElementsInstanced;
|
||||||
|
funcsTable.GL.DrawArraysInstancedBaseInstance = DrawArraysInstancedBaseInstance;
|
||||||
|
funcsTable.GL.DrawArraysInstanced = DrawArraysInstanced;
|
||||||
|
funcsTable.GL.DrawElementsIndirect = DrawElementsIndirect;
|
||||||
|
funcsTable.GL.DrawArraysIndirect = DrawArraysIndirect;
|
||||||
|
funcsTable.GL.Clear = Clear;
|
||||||
|
funcsTable.GL.ClearBufferfi = ClearBufferfi;
|
||||||
|
funcsTable.GL.ClearBufferfv = ClearBufferfv;
|
||||||
|
funcsTable.GL.ClearBufferuiv = ClearBufferuiv;
|
||||||
|
funcsTable.GL.ClearBufferiv = ClearBufferiv;
|
||||||
|
funcsTable.GL.BlitFramebuffer = BlitFramebuffer;
|
||||||
|
funcsTable.GL.CopyTexImage2D = CopyTexImage2D;
|
||||||
|
funcsTable.GL.CopyTexSubImage2D = CopyTexSubImage2D;
|
||||||
|
funcsTable.GL.GenerateMipmap = GenerateMipmap;
|
||||||
|
funcsTable.GL.ReadPixels = ReadPixels;
|
||||||
|
funcsTable.GL.GetTexImage = GetTexImage;
|
||||||
|
funcsTableInitialized = true;
|
||||||
|
}
|
||||||
|
return funcsTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DynamicBackendParameters& BackendObject_DirectVulkanTMP::GetDynamicParameters() const {
|
||||||
|
return m_dynamicParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BackendObject_DirectVulkanTMP::UpdateDynamicBackendParameters() {
|
||||||
|
m_dynamicParameters.UniformBufferOffsetAlignment = m_vulkanCaps.UniformBufferOffsetAlignment;
|
||||||
|
}
|
||||||
|
} // namespace MobileGL::MG_Backend::DirectVulkanTMP
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Backend/DirectVulkanTMP/BackendObject_DirectVulkanTMP.h
|
||||||
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||||
|
// Licensed under the GNU Lesser General Public License v3.0:
|
||||||
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only
|
||||||
|
// End of Source File Header
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <Includes.h>
|
||||||
|
#include "../BackendObject.h"
|
||||||
|
#include <MG_Util/BackendLoaders/Vulkan/Loader.h>
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Backend::DirectVulkanTMP {
|
||||||
|
class BackendObject_DirectVulkanTMP : public BackendObject {
|
||||||
|
public:
|
||||||
|
~BackendObject_DirectVulkanTMP() override;
|
||||||
|
|
||||||
|
void Initialize() override;
|
||||||
|
void InitWindowSurface() override;
|
||||||
|
void InitCapabilities() override;
|
||||||
|
|
||||||
|
const RendererInfo& GetRendererInfo() const override;
|
||||||
|
String GetBackendAPIVersionString() const override;
|
||||||
|
const GlobalBackendFunctionsTable& GetBackendFunctions() const override;
|
||||||
|
const DynamicBackendParameters& GetDynamicParameters() const override;
|
||||||
|
BackendType GetBackendType() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void UpdateDynamicBackendParameters();
|
||||||
|
|
||||||
|
Bool m_initialized = false;
|
||||||
|
DynamicBackendParameters m_dynamicParameters;
|
||||||
|
MG_External::VulkanCapabilities m_vulkanCaps;
|
||||||
|
};
|
||||||
|
} // namespace MobileGL::MG_Backend::DirectVulkanTMP
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Backend/DirectVulkanTMP/DirectVulkanTMP.cpp
|
||||||
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||||
|
// Licensed under the GNU Lesser General Public License v3.0:
|
||||||
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only
|
||||||
|
// End of Source File Header
|
||||||
|
|
||||||
|
#include "DirectVulkanTMP.h"
|
||||||
|
#include "TmpImpl.h"
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Backend::DirectVulkanTMP {
|
||||||
|
void Clear(GLbitfield mask) {
|
||||||
|
MobileGL::MG_Backend::DirectVulkanTMP::TmpImpl::Clear(mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices) {
|
||||||
|
MobileGL::MG_Backend::DirectVulkanTMP::TmpImpl::DrawElements(mode, count, type, indices);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) {}
|
||||||
|
void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value) {}
|
||||||
|
void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value) {}
|
||||||
|
void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value) {}
|
||||||
|
void DrawArrays(GLenum mode, GLint first, GLsizei count) {}
|
||||||
|
void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices, GLint basevertex) {}
|
||||||
|
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
|
||||||
|
GLsizei drawcount) {}
|
||||||
|
void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
|
||||||
|
GLsizei drawcount, const GLint* basevertex) {}
|
||||||
|
void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride) {}
|
||||||
|
void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride) {}
|
||||||
|
void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
|
||||||
|
const void* indices, GLint basevertex) {}
|
||||||
|
void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices) {}
|
||||||
|
void DrawElementsInstancedBaseVertexBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices,
|
||||||
|
GLsizei instancecount, GLint basevertex, GLuint baseinstance) {}
|
||||||
|
void DrawElementsInstancedBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices,
|
||||||
|
GLsizei instancecount, GLint basevertex) {}
|
||||||
|
void DrawElementsInstancedBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices,
|
||||||
|
GLsizei instancecount, GLuint baseinstance) {}
|
||||||
|
void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount) {}
|
||||||
|
void DrawElementsIndirect(GLenum mode, GLenum type, const void* indirect) {}
|
||||||
|
void DrawArraysInstancedBaseInstance(GLenum mode, GLint first, GLsizei count, GLsizei instancecount,
|
||||||
|
GLuint baseinstance) {}
|
||||||
|
void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount) {}
|
||||||
|
void DrawArraysIndirect(GLenum mode, const void* indirect) {}
|
||||||
|
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1,
|
||||||
|
GLint dstY1, GLbitfield mask, GLenum filter) {}
|
||||||
|
void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width,
|
||||||
|
GLsizei height, GLint border) {}
|
||||||
|
void CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width,
|
||||||
|
GLsizei height) {}
|
||||||
|
void GenerateMipmap(GLenum target) {}
|
||||||
|
void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) {}
|
||||||
|
void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) {}
|
||||||
|
|
||||||
|
Bool InitWindowSurface(NativeWindowType window) {
|
||||||
|
if (!window) {
|
||||||
|
MGLOG_E("Cannot initialize Vulkan window surface: invalid window handle");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
TmpImpl::InitVulkan(window);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Present() {
|
||||||
|
TmpImpl::Present();
|
||||||
|
}
|
||||||
|
|
||||||
|
const TmpImpl::VulkanState& GetVulkanState() {
|
||||||
|
return TmpImpl::GetVulkanState();
|
||||||
|
}
|
||||||
|
} // namespace MobileGL::MG_Backend::DirectVulkanTMP
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Backend/DirectVulkanTMP/DirectVulkanTMP.h
|
||||||
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||||
|
// Licensed under the GNU Lesser General Public License v3.0:
|
||||||
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only
|
||||||
|
// End of Source File Header
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <Includes.h>
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Backend::DirectVulkanTMP {
|
||||||
|
namespace TmpImpl {
|
||||||
|
class VulkanState;
|
||||||
|
} // namespace TmpImpl
|
||||||
|
|
||||||
|
void ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
|
||||||
|
void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value);
|
||||||
|
void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value);
|
||||||
|
void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value);
|
||||||
|
void Clear(GLbitfield mask);
|
||||||
|
void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices);
|
||||||
|
void DrawArrays(GLenum mode, GLint first, GLsizei count);
|
||||||
|
void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices, GLint basevertex);
|
||||||
|
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
|
||||||
|
GLsizei drawcount);
|
||||||
|
void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
|
||||||
|
GLsizei drawcount, const GLint* basevertex);
|
||||||
|
void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride);
|
||||||
|
void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride);
|
||||||
|
void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
|
||||||
|
const void* indices, GLint basevertex);
|
||||||
|
void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices);
|
||||||
|
void DrawElementsInstancedBaseVertexBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices,
|
||||||
|
GLsizei instancecount, GLint basevertex, GLuint baseinstance);
|
||||||
|
void DrawElementsInstancedBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices,
|
||||||
|
GLsizei instancecount, GLint basevertex);
|
||||||
|
void DrawElementsInstancedBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices,
|
||||||
|
GLsizei instancecount, GLuint baseinstance);
|
||||||
|
void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount);
|
||||||
|
void DrawElementsIndirect(GLenum mode, GLenum type, const void* indirect);
|
||||||
|
void DrawArraysInstancedBaseInstance(GLenum mode, GLint first, GLsizei count, GLsizei instancecount,
|
||||||
|
GLuint baseinstance);
|
||||||
|
void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount);
|
||||||
|
void DrawArraysIndirect(GLenum mode, const void* indirect);
|
||||||
|
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1,
|
||||||
|
GLint dstY1, GLbitfield mask, GLenum filter);
|
||||||
|
void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width,
|
||||||
|
GLsizei height, GLint border);
|
||||||
|
void CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width,
|
||||||
|
GLsizei height);
|
||||||
|
void GenerateMipmap(GLenum target);
|
||||||
|
void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels);
|
||||||
|
void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels);
|
||||||
|
Bool InitWindowSurface(NativeWindowType window);
|
||||||
|
void Present();
|
||||||
|
const TmpImpl::VulkanState& GetVulkanState();
|
||||||
|
} // namespace MobileGL::MG_Backend::DirectVulkanTMP
|
||||||
@@ -0,0 +1,447 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Backend/DirectVulkanTMP/Managers/ProgramManager.cpp
|
||||||
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||||
|
// Licensed under the GNU Lesser General Public License v3.0:
|
||||||
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only
|
||||||
|
// End of Source File Header
|
||||||
|
|
||||||
|
#include "ProgramManager.h"
|
||||||
|
|
||||||
|
#include "MG_Util/Debug/Log.h"
|
||||||
|
#include "spirv-tools/libspirv.h"
|
||||||
|
#include "spirv-tools/optimizer.hpp"
|
||||||
|
#include "source/opt/constants.h"
|
||||||
|
#include "source/opt/instruction.h"
|
||||||
|
#include "source/opt/ir_builder.h"
|
||||||
|
#include "source/opt/ir_context.h"
|
||||||
|
#include "source/opt/module.h"
|
||||||
|
#include "source/opt/pass.h"
|
||||||
|
#include "source/opt/type_manager.h"
|
||||||
|
|
||||||
|
#include <bit>
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Backend::DirectVulkanTMP::VkManager {
|
||||||
|
namespace {
|
||||||
|
using ProgramObject = MG_State::GLState::ProgramObject;
|
||||||
|
using ShaderObject = MG_State::GLState::ShaderObject;
|
||||||
|
|
||||||
|
struct PositionTargetInfo {
|
||||||
|
uint32_t variableId = 0;
|
||||||
|
uint32_t vectorTypeId = 0;
|
||||||
|
uint32_t floatTypeId = 0;
|
||||||
|
uint32_t vectorPtrTypeId = 0;
|
||||||
|
uint32_t memberIndex = 0;
|
||||||
|
bool isMember = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool IsVec4Float32(spvtools::opt::IRContext* context, uint32_t typeId, uint32_t* outFloatTypeId) {
|
||||||
|
auto* vecInst = context->get_def_use_mgr()->GetDef(typeId);
|
||||||
|
if (!vecInst || vecInst->opcode() != spv::Op::OpTypeVector) return false;
|
||||||
|
if (vecInst->GetSingleWordInOperand(1) != 4) return false;
|
||||||
|
|
||||||
|
const uint32_t floatTypeId = vecInst->GetSingleWordInOperand(0);
|
||||||
|
auto* floatInst = context->get_def_use_mgr()->GetDef(floatTypeId);
|
||||||
|
if (!floatInst || floatInst->opcode() != spv::Op::OpTypeFloat) return false;
|
||||||
|
if (floatInst->GetSingleWordInOperand(0) != 32) return false;
|
||||||
|
|
||||||
|
if (outFloatTypeId) *outFloatTypeId = floatTypeId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ResolveDirectPositionTarget(spvtools::opt::IRContext* context, uint32_t variableId,
|
||||||
|
PositionTargetInfo* outTarget) {
|
||||||
|
auto* varInst = context->get_def_use_mgr()->GetDef(variableId);
|
||||||
|
if (!varInst || varInst->opcode() != spv::Op::OpVariable) return false;
|
||||||
|
if (varInst->GetSingleWordInOperand(0) != static_cast<uint32_t>(spv::StorageClass::Output)) return false;
|
||||||
|
|
||||||
|
auto* ptrTypeInst = context->get_def_use_mgr()->GetDef(varInst->type_id());
|
||||||
|
if (!ptrTypeInst || ptrTypeInst->opcode() != spv::Op::OpTypePointer) return false;
|
||||||
|
if (ptrTypeInst->GetSingleWordInOperand(0) != static_cast<uint32_t>(spv::StorageClass::Output))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
PositionTargetInfo target{};
|
||||||
|
target.variableId = variableId;
|
||||||
|
target.vectorTypeId = ptrTypeInst->GetSingleWordInOperand(1);
|
||||||
|
if (!IsVec4Float32(context, target.vectorTypeId, &target.floatTypeId)) return false;
|
||||||
|
target.vectorPtrTypeId = varInst->type_id();
|
||||||
|
target.isMember = false;
|
||||||
|
|
||||||
|
*outTarget = target;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t FindOutputVectorPointerTypeId(spvtools::opt::IRContext* context, uint32_t vectorTypeId) {
|
||||||
|
auto* vectorType = context->get_type_mgr()->GetType(vectorTypeId);
|
||||||
|
if (!vectorType) return 0;
|
||||||
|
spvtools::opt::analysis::Pointer ptrType(vectorType, spv::StorageClass::Output);
|
||||||
|
return context->get_type_mgr()->GetTypeInstruction(&ptrType);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ResolveMemberPositionTarget(spvtools::opt::IRContext* context, uint32_t structTypeId, uint32_t memberIndex,
|
||||||
|
PositionTargetInfo* outTarget) {
|
||||||
|
auto* structInst = context->get_def_use_mgr()->GetDef(structTypeId);
|
||||||
|
if (!structInst || structInst->opcode() != spv::Op::OpTypeStruct) return false;
|
||||||
|
if (memberIndex >= structInst->NumInOperands()) return false;
|
||||||
|
|
||||||
|
const uint32_t vectorTypeId = structInst->GetSingleWordInOperand(memberIndex);
|
||||||
|
uint32_t floatTypeId = 0;
|
||||||
|
if (!IsVec4Float32(context, vectorTypeId, &floatTypeId)) return false;
|
||||||
|
|
||||||
|
const uint32_t vectorPtrTypeId = FindOutputVectorPointerTypeId(context, vectorTypeId);
|
||||||
|
if (vectorPtrTypeId == 0) return false;
|
||||||
|
|
||||||
|
for (auto& inst : context->module()->types_values()) {
|
||||||
|
if (inst.opcode() != spv::Op::OpVariable) continue;
|
||||||
|
if (inst.GetSingleWordInOperand(0) != static_cast<uint32_t>(spv::StorageClass::Output)) continue;
|
||||||
|
|
||||||
|
auto* ptrTypeInst = context->get_def_use_mgr()->GetDef(inst.type_id());
|
||||||
|
if (!ptrTypeInst || ptrTypeInst->opcode() != spv::Op::OpTypePointer) continue;
|
||||||
|
if (ptrTypeInst->GetSingleWordInOperand(0) != static_cast<uint32_t>(spv::StorageClass::Output))
|
||||||
|
continue;
|
||||||
|
if (ptrTypeInst->GetSingleWordInOperand(1) != structTypeId) continue;
|
||||||
|
|
||||||
|
PositionTargetInfo target{};
|
||||||
|
target.variableId = inst.result_id();
|
||||||
|
target.vectorTypeId = vectorTypeId;
|
||||||
|
target.floatTypeId = floatTypeId;
|
||||||
|
target.vectorPtrTypeId = vectorPtrTypeId;
|
||||||
|
target.memberIndex = memberIndex;
|
||||||
|
target.isMember = true;
|
||||||
|
*outTarget = target;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FindPositionTarget(spvtools::opt::IRContext* context, PositionTargetInfo* outTarget) {
|
||||||
|
Vector<Pair<uint32_t, uint32_t>> memberCandidates;
|
||||||
|
constexpr uint32_t kDecorationBuiltIn = static_cast<uint32_t>(spv::Decoration::BuiltIn);
|
||||||
|
constexpr uint32_t kBuiltInPosition = static_cast<uint32_t>(spv::BuiltIn::Position);
|
||||||
|
|
||||||
|
for (auto& inst : context->module()->annotations()) {
|
||||||
|
if (inst.opcode() == spv::Op::OpDecorate) {
|
||||||
|
if (inst.NumInOperands() < 3) continue;
|
||||||
|
if (inst.GetSingleWordInOperand(1) != kDecorationBuiltIn) continue;
|
||||||
|
if (inst.GetSingleWordInOperand(2) != kBuiltInPosition) continue;
|
||||||
|
if (ResolveDirectPositionTarget(context, inst.GetSingleWordInOperand(0), outTarget)) return true;
|
||||||
|
} else if (inst.opcode() == spv::Op::OpMemberDecorate) {
|
||||||
|
if (inst.NumInOperands() < 4) continue;
|
||||||
|
if (inst.GetSingleWordInOperand(2) != kDecorationBuiltIn) continue;
|
||||||
|
if (inst.GetSingleWordInOperand(3) != kBuiltInPosition) continue;
|
||||||
|
memberCandidates.emplace_back(inst.GetSingleWordInOperand(0), inst.GetSingleWordInOperand(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& [structTypeId, memberIndex] : memberCandidates) {
|
||||||
|
if (ResolveMemberPositionTarget(context, structTypeId, memberIndex, outTarget)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InsertPositionFixup(spvtools::opt::IRContext* context, spvtools::opt::Instruction* insertBefore,
|
||||||
|
const PositionTargetInfo& target, uint32_t halfConstId, bool doYFlip, bool doZRemap) {
|
||||||
|
using namespace spvtools::opt;
|
||||||
|
InstructionBuilder builder(context, insertBefore,
|
||||||
|
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
|
||||||
|
|
||||||
|
uint32_t positionPtrId = target.variableId;
|
||||||
|
if (target.isMember) {
|
||||||
|
const uint32_t memberIndexId = builder.GetUintConstantId(target.memberIndex);
|
||||||
|
if (memberIndexId == 0) return false;
|
||||||
|
auto* access = builder.AddAccessChain(target.vectorPtrTypeId, target.variableId, {memberIndexId});
|
||||||
|
if (!access) return false;
|
||||||
|
positionPtrId = access->result_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* position = builder.AddLoad(target.vectorTypeId, positionPtrId);
|
||||||
|
if (!position) return false;
|
||||||
|
auto* x = builder.AddCompositeExtract(target.floatTypeId, position->result_id(), {0});
|
||||||
|
auto* y = builder.AddCompositeExtract(target.floatTypeId, position->result_id(), {1});
|
||||||
|
auto* z = builder.AddCompositeExtract(target.floatTypeId, position->result_id(), {2});
|
||||||
|
auto* w = builder.AddCompositeExtract(target.floatTypeId, position->result_id(), {3});
|
||||||
|
if (!x || !y || !z || !w) return false;
|
||||||
|
|
||||||
|
if (!doYFlip && !doZRemap) return false;
|
||||||
|
|
||||||
|
uint32_t yValueId = y->result_id();
|
||||||
|
if (doYFlip) {
|
||||||
|
auto* negY = builder.AddUnaryOp(target.floatTypeId, spv::Op::OpFNegate, y->result_id());
|
||||||
|
if (!negY) return false;
|
||||||
|
yValueId = negY->result_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t zValueId = z->result_id();
|
||||||
|
if (doZRemap) {
|
||||||
|
auto* zPlusW = builder.AddBinaryOp(target.floatTypeId, spv::Op::OpFAdd, z->result_id(), w->result_id());
|
||||||
|
if (!zPlusW) return false;
|
||||||
|
auto* mappedZ =
|
||||||
|
builder.AddBinaryOp(target.floatTypeId, spv::Op::OpFMul, zPlusW->result_id(), halfConstId);
|
||||||
|
if (!mappedZ) return false;
|
||||||
|
zValueId = mappedZ->result_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* fixedPosition = builder.AddCompositeConstruct(target.vectorTypeId,
|
||||||
|
{x->result_id(), yValueId, zValueId, w->result_id()});
|
||||||
|
if (!fixedPosition) return false;
|
||||||
|
|
||||||
|
return builder.AddStore(positionPtrId, fixedPosition->result_id()) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
class GlToVulkanPositionFixPass final : public spvtools::opt::Pass {
|
||||||
|
public:
|
||||||
|
const char* name() const override { return "gl-to-vulkan-position-fix"; }
|
||||||
|
explicit GlToVulkanPositionFixPass(ShaderTransformFlags transformFlags)
|
||||||
|
: m_transformFlags(transformFlags) {}
|
||||||
|
|
||||||
|
Status Process() override {
|
||||||
|
if (!m_transformFlags) return Status::SuccessWithoutChange;
|
||||||
|
PositionTargetInfo target{};
|
||||||
|
if (!FindPositionTarget(context(), &target)) return Status::SuccessWithoutChange;
|
||||||
|
|
||||||
|
auto* floatType = context()->get_type_mgr()->GetType(target.floatTypeId);
|
||||||
|
if (!floatType) return Status::SuccessWithoutChange;
|
||||||
|
|
||||||
|
const uint32_t halfBits = std::bit_cast<uint32_t>(0.5f);
|
||||||
|
const auto* halfConst = context()->get_constant_mgr()->GetConstant(floatType, {halfBits});
|
||||||
|
auto* halfInst = context()->get_constant_mgr()->GetDefiningInstruction(halfConst);
|
||||||
|
if (!halfInst) return Status::SuccessWithoutChange;
|
||||||
|
const uint32_t halfConstId = halfInst->result_id();
|
||||||
|
|
||||||
|
const bool doYFlip = (m_transformFlags & ShaderTransformBit::PositionYFlip);
|
||||||
|
const bool doZRemap = (m_transformFlags & ShaderTransformBit::PositionZRemap);
|
||||||
|
|
||||||
|
bool modified = false;
|
||||||
|
for (auto& entryPoint : get_module()->entry_points()) {
|
||||||
|
if (entryPoint.opcode() != spv::Op::OpEntryPoint) continue;
|
||||||
|
if (entryPoint.NumInOperands() < 2) continue;
|
||||||
|
|
||||||
|
const auto model = static_cast<spv::ExecutionModel>(entryPoint.GetSingleWordInOperand(0));
|
||||||
|
if (model != spv::ExecutionModel::Vertex && model != spv::ExecutionModel::TessellationEvaluation &&
|
||||||
|
model != spv::ExecutionModel::Geometry) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* function = context()->GetFunction(entryPoint.GetSingleWordInOperand(1));
|
||||||
|
if (!function) continue;
|
||||||
|
|
||||||
|
for (auto& bb : *function) {
|
||||||
|
for (auto instIter = bb.begin(); instIter != bb.end(); ++instIter) {
|
||||||
|
auto* inst = &*instIter;
|
||||||
|
const bool needsFixup =
|
||||||
|
(model == spv::ExecutionModel::Geometry && inst->opcode() == spv::Op::OpEmitVertex) ||
|
||||||
|
(model != spv::ExecutionModel::Geometry && inst->opcode() == spv::Op::OpReturn);
|
||||||
|
if (!needsFixup) continue;
|
||||||
|
|
||||||
|
modified |= InsertPositionFixup(context(), inst, target, halfConstId, doYFlip, doZRemap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!modified) return Status::SuccessWithoutChange;
|
||||||
|
context()->InvalidateAnalysesExceptFor(spvtools::opt::IRContext::kAnalysisDefUse |
|
||||||
|
spvtools::opt::IRContext::kAnalysisInstrToBlockMapping);
|
||||||
|
return Status::SuccessWithChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ShaderTransformFlags m_transformFlags;
|
||||||
|
};
|
||||||
|
|
||||||
|
spvtools::Optimizer::PassToken CreateGlToVulkanPositionFixPass(ShaderTransformFlags transformFlags) {
|
||||||
|
return spvtools::Optimizer::PassToken(MakeUnique<GlToVulkanPositionFixPass>(transformFlags));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TransformSpirvForVulkanPositionFix(const Vector<Uint32>& input, Vector<Uint32>& output,
|
||||||
|
ShaderTransformFlags transformFlags) {
|
||||||
|
if (input.empty()) {
|
||||||
|
output.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!transformFlags) {
|
||||||
|
output = input;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
spvtools::Optimizer optimizer(SPV_ENV_VULKAN_1_3);
|
||||||
|
spvtools::OptimizerOptions options;
|
||||||
|
options.set_run_validator(false);
|
||||||
|
optimizer.RegisterPass(CreateGlToVulkanPositionFixPass(transformFlags));
|
||||||
|
|
||||||
|
const bool success = optimizer.Run(input.data(), input.size(), &output, options);
|
||||||
|
if (!success) {
|
||||||
|
MGLOG_E("Vulkan: failed to run GL->Vulkan position fix pass");
|
||||||
|
output = input;
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderStage PickClipFixupStage(const Vector<SharedPtr<ShaderObject>>& shaders) {
|
||||||
|
bool hasGeometry = false;
|
||||||
|
bool hasTessEval = false;
|
||||||
|
bool hasVertex = false;
|
||||||
|
|
||||||
|
for (const auto& shader : shaders) {
|
||||||
|
if (!shader) continue;
|
||||||
|
const auto stage = shader->GetShaderStage();
|
||||||
|
hasGeometry |= (stage == ShaderStage::Geometry);
|
||||||
|
hasTessEval |= (stage == ShaderStage::TessEval);
|
||||||
|
hasVertex |= (stage == ShaderStage::Vertex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasGeometry) return ShaderStage::Geometry;
|
||||||
|
if (hasTessEval) return ShaderStage::TessEval;
|
||||||
|
if (hasVertex) return ShaderStage::Vertex;
|
||||||
|
return ShaderStage::Unknown;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
ProgramManager::~ProgramManager() {
|
||||||
|
for (auto& [_, stages] : m_cache) {
|
||||||
|
DestroyStages(stages);
|
||||||
|
}
|
||||||
|
m_cache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramManager::HashType ProgramManager::ComputeSourceSpvHash(MG_State::GLState::ProgramObject* program) const {
|
||||||
|
if (!program) return 0;
|
||||||
|
XXH64_state_t* state = XXH64_createState();
|
||||||
|
XXH64_reset(state, 0xC0FFEEu);
|
||||||
|
auto& spirvs = program->GetGeneratedSpirv();
|
||||||
|
for (const auto& spv : spirvs) {
|
||||||
|
if (spv.empty()) continue;
|
||||||
|
XXH64_update(state, spv.data(), spv.size() * sizeof(Uint));
|
||||||
|
}
|
||||||
|
HashType hash = XXH64_digest(state);
|
||||||
|
XXH64_freeState(state);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramManager::HashType ProgramManager::ComputeSpvHash(const Vector<Vector<Uint32>>& spirvs) const {
|
||||||
|
XXH64_state_t* state = XXH64_createState();
|
||||||
|
XXH64_reset(state, 0xC0FFEEu);
|
||||||
|
for (const auto& spv : spirvs) {
|
||||||
|
if (spv.empty()) continue;
|
||||||
|
XXH64_update(state, spv.data(), spv.size() * sizeof(Uint32));
|
||||||
|
}
|
||||||
|
HashType hash = XXH64_digest(state);
|
||||||
|
XXH64_freeState(state);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgramManager::BuildPipelineSpirvModules(MG_State::GLState::ProgramObject* program,
|
||||||
|
Vector<Vector<Uint32>>& outSpirvs,
|
||||||
|
ShaderTransformFlags transformFlags) const {
|
||||||
|
outSpirvs.clear();
|
||||||
|
if (!program) return;
|
||||||
|
|
||||||
|
auto& spirvs = program->GetGeneratedSpirv();
|
||||||
|
auto& shaders = program->GetAttachedShaders();
|
||||||
|
const ShaderStage fixupStage = PickClipFixupStage(shaders);
|
||||||
|
|
||||||
|
outSpirvs.reserve(spirvs.size());
|
||||||
|
for (SizeT i = 0; i < spirvs.size(); ++i) {
|
||||||
|
Vector<Uint32> module = spirvs[i];
|
||||||
|
|
||||||
|
ShaderStage stage = ShaderStage::Unknown;
|
||||||
|
if (i < shaders.size() && shaders[i]) stage = shaders[i]->GetShaderStage();
|
||||||
|
|
||||||
|
if (!module.empty() && fixupStage != ShaderStage::Unknown && stage == fixupStage) {
|
||||||
|
Vector<Uint32> transformed;
|
||||||
|
TransformSpirvForVulkanPositionFix(module, transformed, transformFlags);
|
||||||
|
module = Move(transformed);
|
||||||
|
}
|
||||||
|
outSpirvs.push_back(Move(module));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramManager::HashType ProgramManager::ComputeSpvHash(MG_State::GLState::ProgramObject* program,
|
||||||
|
ShaderTransformFlags transformFlags) const {
|
||||||
|
Vector<Vector<Uint32>> spirvs;
|
||||||
|
BuildPipelineSpirvModules(program, spirvs, transformFlags);
|
||||||
|
return ComputeSpvHash(spirvs);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramManager::HashType ProgramManager::ComputeProgramHash(MG_State::GLState::ProgramObject* program,
|
||||||
|
ShaderTransformFlags transformFlags) const {
|
||||||
|
if (!program) return 0;
|
||||||
|
const HashType sourceHash = ComputeSourceSpvHash(program);
|
||||||
|
auto it = m_cache.find(program);
|
||||||
|
if (it != m_cache.end() && it->second.sourceHash == sourceHash) return it->second.hash;
|
||||||
|
return ComputeSpvHash(program, transformFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkShaderStageFlagBits ProgramManager::ToVkStage(ShaderStage stage) const {
|
||||||
|
switch (stage) {
|
||||||
|
case ShaderStage::Vertex:
|
||||||
|
return VK_SHADER_STAGE_VERTEX_BIT;
|
||||||
|
case ShaderStage::Fragment:
|
||||||
|
return VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||||
|
case ShaderStage::Geometry:
|
||||||
|
return VK_SHADER_STAGE_GEOMETRY_BIT;
|
||||||
|
case ShaderStage::TessControl:
|
||||||
|
return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
|
||||||
|
case ShaderStage::TessEval:
|
||||||
|
return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
|
||||||
|
case ShaderStage::Compute:
|
||||||
|
return VK_SHADER_STAGE_COMPUTE_BIT;
|
||||||
|
default:
|
||||||
|
return VK_SHADER_STAGE_ALL_GRAPHICS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProgramManager::DestroyStages(ProgramStages& stages) {
|
||||||
|
for (auto module : stages.modules) {
|
||||||
|
if (module != VK_NULL_HANDLE) {
|
||||||
|
vkDestroyShaderModule(m_ctx.GetDevice(), module, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stages.modules.clear();
|
||||||
|
stages.stages.clear();
|
||||||
|
stages.sourceHash = 0;
|
||||||
|
stages.hash = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<VkPipelineShaderStageCreateInfo>& ProgramManager::CreatePipelineShaderStages(
|
||||||
|
MG_State::GLState::ProgramObject* program, ShaderTransformFlags transformFlags) {
|
||||||
|
auto& entry = m_cache[program];
|
||||||
|
HashType sourceHash = ComputeSourceSpvHash(program);
|
||||||
|
if (!entry.stages.empty() && entry.sourceHash == sourceHash) return entry.stages;
|
||||||
|
|
||||||
|
DestroyStages(entry);
|
||||||
|
entry.sourceHash = sourceHash;
|
||||||
|
|
||||||
|
if (!program) return entry.stages;
|
||||||
|
|
||||||
|
Vector<Vector<Uint32>> spirvs;
|
||||||
|
BuildPipelineSpirvModules(program, spirvs, transformFlags);
|
||||||
|
entry.hash = ComputeSpvHash(spirvs);
|
||||||
|
auto& shaders = program->GetAttachedShaders();
|
||||||
|
|
||||||
|
for (SizeT i = 0; i < spirvs.size(); ++i) {
|
||||||
|
auto& spv = spirvs[i];
|
||||||
|
if (spv.empty()) continue;
|
||||||
|
|
||||||
|
VkShaderModuleCreateInfo smci{VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO};
|
||||||
|
smci.codeSize = spv.size() * sizeof(Uint);
|
||||||
|
smci.pCode = spv.data();
|
||||||
|
|
||||||
|
VkShaderModule module = VK_NULL_HANDLE;
|
||||||
|
VK_VERIFY(vkCreateShaderModule(m_ctx.GetDevice(), &smci, nullptr, &module), "vkCreateShaderModule");
|
||||||
|
|
||||||
|
VkPipelineShaderStageCreateInfo stage{VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO};
|
||||||
|
ShaderStage shaderStage = ShaderStage::Unknown;
|
||||||
|
if (i < shaders.size() && shaders[i]) shaderStage = shaders[i]->GetShaderStage();
|
||||||
|
stage.stage = ToVkStage(shaderStage);
|
||||||
|
stage.module = module;
|
||||||
|
stage.pName = "main";
|
||||||
|
|
||||||
|
entry.modules.push_back(module);
|
||||||
|
entry.stages.push_back(stage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry.stages;
|
||||||
|
}
|
||||||
|
} // namespace MobileGL::MG_Backend::DirectVulkanTMP::VkManager
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Backend/DirectVulkanTMP/Managers/ProgramManager.h
|
||||||
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||||
|
// Licensed under the GNU Lesser General Public License v3.0:
|
||||||
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only
|
||||||
|
// End of Source File Header
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <Includes.h>
|
||||||
|
#include "../Renderer/VulkanContext.h"
|
||||||
|
#include "../Renderer/VkCommon.h"
|
||||||
|
#include "MG_State/GLState/ProgramState/ProgramObject.h"
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Backend::DirectVulkanTMP::VkManager {
|
||||||
|
enum class ShaderTransformBit : Uint {
|
||||||
|
None = 0,
|
||||||
|
PositionYFlip = 1 << 0,
|
||||||
|
PositionZRemap = 1 << 1,
|
||||||
|
};
|
||||||
|
using ShaderTransformFlags = Flags<ShaderTransformBit>;
|
||||||
|
|
||||||
|
class ProgramManager {
|
||||||
|
public:
|
||||||
|
using HashType = Uint64;
|
||||||
|
|
||||||
|
explicit ProgramManager(VulkanContext& ctx) : m_ctx(ctx) {}
|
||||||
|
~ProgramManager();
|
||||||
|
|
||||||
|
ProgramManager(const ProgramManager&) = delete;
|
||||||
|
ProgramManager& operator=(const ProgramManager&) = delete;
|
||||||
|
|
||||||
|
Vector<VkPipelineShaderStageCreateInfo>& CreatePipelineShaderStages(
|
||||||
|
MG_State::GLState::ProgramObject* program,
|
||||||
|
ShaderTransformFlags transformFlags = ShaderTransformBit::PositionZRemap);
|
||||||
|
HashType ComputeProgramHash(MG_State::GLState::ProgramObject* program,
|
||||||
|
ShaderTransformFlags transformFlags = ShaderTransformBit::PositionZRemap) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct ProgramStages {
|
||||||
|
HashType sourceHash = 0;
|
||||||
|
HashType hash = 0;
|
||||||
|
Vector<VkPipelineShaderStageCreateInfo> stages;
|
||||||
|
Vector<VkShaderModule> modules;
|
||||||
|
};
|
||||||
|
|
||||||
|
void DestroyStages(ProgramStages& stages);
|
||||||
|
HashType ComputeSourceSpvHash(MG_State::GLState::ProgramObject* program) const;
|
||||||
|
HashType ComputeSpvHash(MG_State::GLState::ProgramObject* program, ShaderTransformFlags transformFlags) const;
|
||||||
|
HashType ComputeSpvHash(const Vector<Vector<Uint32>>& spirvs) const;
|
||||||
|
void BuildPipelineSpirvModules(MG_State::GLState::ProgramObject* program, Vector<Vector<Uint32>>& outSpirvs,
|
||||||
|
ShaderTransformFlags transformFlags) const;
|
||||||
|
VkShaderStageFlagBits ToVkStage(ShaderStage stage) const;
|
||||||
|
|
||||||
|
VulkanContext& m_ctx;
|
||||||
|
UnorderedMap<const MG_State::GLState::ProgramObject*, ProgramStages> m_cache;
|
||||||
|
};
|
||||||
|
} // namespace MobileGL::MG_Backend::DirectVulkanTMP::VkManager
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Backend/DirectVulkanTMP/Renderer/FrameContext.cpp
|
||||||
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||||
|
// Licensed under the GNU Lesser General Public License v3.0:
|
||||||
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only
|
||||||
|
// End of Source File Header
|
||||||
|
|
||||||
|
#include "FrameContext.h"
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Backend::DirectVulkanTMP::VkManager {
|
||||||
|
void FrameContext::Initialize(VulkanContext& ctx, VkCommandPool pool) {
|
||||||
|
CommandPool = pool;
|
||||||
|
VkCommandBufferAllocateInfo abci{VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO};
|
||||||
|
abci.commandPool = pool;
|
||||||
|
abci.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||||
|
abci.commandBufferCount = 1;
|
||||||
|
VK_VERIFY(vkAllocateCommandBuffers(ctx.GetDevice(), &abci, &CommandBuffer), "vkAllocateCommandBuffers");
|
||||||
|
|
||||||
|
VkSemaphoreCreateInfo sci{VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
|
||||||
|
VK_VERIFY(vkCreateSemaphore(ctx.GetDevice(), &sci, nullptr, &ImageAvailable), "vkCreateSemaphore");
|
||||||
|
VK_VERIFY(vkCreateSemaphore(ctx.GetDevice(), &sci, nullptr, &RenderFinished), "vkCreateSemaphore");
|
||||||
|
|
||||||
|
VkFenceCreateInfo fci{VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
|
||||||
|
fci.flags = VK_FENCE_CREATE_SIGNALED_BIT;
|
||||||
|
VK_VERIFY(vkCreateFence(ctx.GetDevice(), &fci, nullptr, &InFlightFence), "vkCreateFence");
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrameContext::Cleanup(VulkanContext& ctx) {
|
||||||
|
auto device = ctx.GetDevice();
|
||||||
|
if (InFlightFence != VK_NULL_HANDLE) {
|
||||||
|
vkDestroyFence(device, InFlightFence, nullptr);
|
||||||
|
InFlightFence = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
if (ImageAvailable != VK_NULL_HANDLE) {
|
||||||
|
vkDestroySemaphore(device, ImageAvailable, nullptr);
|
||||||
|
ImageAvailable = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
if (RenderFinished != VK_NULL_HANDLE) {
|
||||||
|
vkDestroySemaphore(device, RenderFinished, nullptr);
|
||||||
|
RenderFinished = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
if (CommandBuffer != VK_NULL_HANDLE) {
|
||||||
|
vkFreeCommandBuffers(device, CommandPool, 1, &CommandBuffer);
|
||||||
|
CommandBuffer = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
CommandPool = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
} // namespace MobileGL::MG_Backend::DirectVulkanTMP::VkManager
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Backend/DirectVulkanTMP/Renderer/FrameContext.h
|
||||||
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||||
|
// Licensed under the GNU Lesser General Public License v3.0:
|
||||||
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only
|
||||||
|
// End of Source File Header
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <Includes.h>
|
||||||
|
#include "VulkanContext.h"
|
||||||
|
#include "VkCommon.h"
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Backend::DirectVulkanTMP {
|
||||||
|
struct TrashBuffer {
|
||||||
|
VkBuffer buffer = VK_NULL_HANDLE;
|
||||||
|
VkDeviceMemory memory = VK_NULL_HANDLE;
|
||||||
|
Bool mapped = false;
|
||||||
|
};
|
||||||
|
struct TrashImage {
|
||||||
|
VkImage image = VK_NULL_HANDLE;
|
||||||
|
VkDeviceMemory memory = VK_NULL_HANDLE;
|
||||||
|
VkImageView view = VK_NULL_HANDLE;
|
||||||
|
Vector<VkImageView> mipViews;
|
||||||
|
};
|
||||||
|
namespace VkManager {
|
||||||
|
struct FrameContext {
|
||||||
|
VkCommandBuffer CommandBuffer = VK_NULL_HANDLE;
|
||||||
|
VkSemaphore ImageAvailable = VK_NULL_HANDLE;
|
||||||
|
VkSemaphore RenderFinished = VK_NULL_HANDLE;
|
||||||
|
VkFence InFlightFence = VK_NULL_HANDLE;
|
||||||
|
Uint32 CurrentImageIndex = 0;
|
||||||
|
VkCommandPool CommandPool = VK_NULL_HANDLE;
|
||||||
|
Vector<TrashBuffer> TrashBuffers;
|
||||||
|
Vector<TrashImage> TrashImages;
|
||||||
|
// VkCommandPool CommandPool = VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
void Initialize(VulkanContext& ctx, VkCommandPool pool);
|
||||||
|
void Cleanup(VulkanContext& ctx);
|
||||||
|
};
|
||||||
|
} // namespace VkManager
|
||||||
|
} // namespace MobileGL::MG_Backend::DirectVulkanTMP
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Backend/DirectVulkanTMP/Renderer/PipelineManager.cpp
|
||||||
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||||
|
// Licensed under the GNU Lesser General Public License v3.0:
|
||||||
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only
|
||||||
|
// End of Source File Header
|
||||||
|
|
||||||
|
#include <Includes.h>
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Backend::DirectVulkanTMP::VkManager {}
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Backend/DirectVulkanTMP/Renderer/SwapchainManager.cpp
|
||||||
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||||
|
// Licensed under the GNU Lesser General Public License v3.0:
|
||||||
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only
|
||||||
|
// End of Source File Header
|
||||||
|
|
||||||
|
#include "SwapchainManager.h"
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Backend::DirectVulkanTMP::VkManager {
|
||||||
|
namespace {
|
||||||
|
VkSurfaceFormatKHR ChooseSurfaceFormat(const Vector<VkSurfaceFormatKHR>& formats) {
|
||||||
|
for (const auto& f : formats) {
|
||||||
|
if ((f.format == VK_FORMAT_R8G8B8A8_UNORM || f.format == VK_FORMAT_B8G8R8A8_UNORM) &&
|
||||||
|
f.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return formats.empty() ? VkSurfaceFormatKHR{VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}
|
||||||
|
: formats[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
VkPresentModeKHR ChoosePresentMode(const Vector<VkPresentModeKHR>& modes) {
|
||||||
|
for (const auto& m : modes) {
|
||||||
|
if (m == VK_PRESENT_MODE_MAILBOX_KHR) return m;
|
||||||
|
}
|
||||||
|
return VK_PRESENT_MODE_FIFO_KHR;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkExtent2D ChooseExtent(const VkSurfaceCapabilitiesKHR& caps, ANativeWindow* window) {
|
||||||
|
if (caps.currentExtent.width != UINT32_MAX) return caps.currentExtent;
|
||||||
|
// try to get frontend viewport size
|
||||||
|
VkExtent2D extent{640, 480};
|
||||||
|
#ifdef VK_USE_PLATFORM_ANDROID_KHR
|
||||||
|
if (window) {
|
||||||
|
extent.width = static_cast<Uint32>(ANativeWindow_getWidth(window));
|
||||||
|
extent.height = static_cast<Uint32>(ANativeWindow_getHeight(window));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void)window;
|
||||||
|
#endif
|
||||||
|
extent.width = std::max(caps.minImageExtent.width, std::min(caps.maxImageExtent.width, extent.width));
|
||||||
|
extent.height = std::max(caps.minImageExtent.height, std::min(caps.maxImageExtent.height, extent.height));
|
||||||
|
return extent;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
SwapchainManager::~SwapchainManager() {
|
||||||
|
DestroySwapchain();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwapchainManager::Initialize() {
|
||||||
|
CreateSwapchain(VK_NULL_HANDLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwapchainManager::Recreate() {
|
||||||
|
DestroySwapchain();
|
||||||
|
CreateSwapchain(VK_NULL_HANDLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwapchainManager::SetFramebuffers(Vector<VkFramebuffer>&& framebuffers) {
|
||||||
|
m_framebuffers = Move(framebuffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwapchainManager::DestroySwapchain() {
|
||||||
|
auto device = m_ctx.GetDevice();
|
||||||
|
if (device == VK_NULL_HANDLE) return;
|
||||||
|
for (auto fb : m_framebuffers) {
|
||||||
|
if (fb != VK_NULL_HANDLE) vkDestroyFramebuffer(device, fb, nullptr);
|
||||||
|
}
|
||||||
|
m_framebuffers.clear();
|
||||||
|
|
||||||
|
for (auto view : m_imageViews) {
|
||||||
|
if (view != VK_NULL_HANDLE) vkDestroyImageView(device, view, nullptr);
|
||||||
|
}
|
||||||
|
m_imageViews.clear();
|
||||||
|
m_images.clear();
|
||||||
|
m_imagesInFlight.clear();
|
||||||
|
|
||||||
|
if (m_swapchain != VK_NULL_HANDLE) {
|
||||||
|
vkDestroySwapchainKHR(device, m_swapchain, nullptr);
|
||||||
|
m_swapchain = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
m_format = VK_FORMAT_UNDEFINED;
|
||||||
|
m_extent = {0, 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwapchainManager::CreateSwapchain(VkSwapchainKHR oldSwapchain) {
|
||||||
|
VkSurfaceCapabilitiesKHR caps{};
|
||||||
|
VK_VERIFY(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_ctx.GetPhysicalDevice(), m_ctx.GetSurface(), &caps),
|
||||||
|
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR");
|
||||||
|
|
||||||
|
Uint32 fmtCount = 0;
|
||||||
|
vkGetPhysicalDeviceSurfaceFormatsKHR(m_ctx.GetPhysicalDevice(), m_ctx.GetSurface(), &fmtCount, nullptr);
|
||||||
|
Vector<VkSurfaceFormatKHR> formats(fmtCount);
|
||||||
|
if (fmtCount > 0) {
|
||||||
|
vkGetPhysicalDeviceSurfaceFormatsKHR(m_ctx.GetPhysicalDevice(), m_ctx.GetSurface(), &fmtCount,
|
||||||
|
formats.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint32 modeCount = 0;
|
||||||
|
vkGetPhysicalDeviceSurfacePresentModesKHR(m_ctx.GetPhysicalDevice(), m_ctx.GetSurface(), &modeCount, nullptr);
|
||||||
|
Vector<VkPresentModeKHR> modes(modeCount);
|
||||||
|
if (modeCount > 0) {
|
||||||
|
vkGetPhysicalDeviceSurfacePresentModesKHR(m_ctx.GetPhysicalDevice(), m_ctx.GetSurface(), &modeCount,
|
||||||
|
modes.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSurfaceFormatKHR surfaceFormat = ChooseSurfaceFormat(formats);
|
||||||
|
VkPresentModeKHR presentMode = ChoosePresentMode(modes);
|
||||||
|
VkExtent2D extent;
|
||||||
|
if (m_viewportSize.x() == 0 || m_viewportSize.y() == 0) {
|
||||||
|
extent = ChooseExtent(caps, m_ctx.GetWindow());
|
||||||
|
} else {
|
||||||
|
extent = {m_viewportSize.x(), m_viewportSize.y()};
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint32 imageCount = caps.minImageCount + 1;
|
||||||
|
if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount) imageCount = caps.maxImageCount;
|
||||||
|
|
||||||
|
VkSwapchainCreateInfoKHR sci{VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR};
|
||||||
|
sci.surface = m_ctx.GetSurface();
|
||||||
|
sci.minImageCount = imageCount;
|
||||||
|
sci.imageFormat = surfaceFormat.format;
|
||||||
|
sci.imageColorSpace = surfaceFormat.colorSpace;
|
||||||
|
sci.imageExtent = extent;
|
||||||
|
sci.imageArrayLayers = 1;
|
||||||
|
sci.imageUsage =
|
||||||
|
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||||
|
sci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
|
sci.preTransform = caps.currentTransform;
|
||||||
|
sci.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||||
|
sci.presentMode = presentMode;
|
||||||
|
sci.clipped = VK_TRUE;
|
||||||
|
sci.oldSwapchain = oldSwapchain;
|
||||||
|
|
||||||
|
VK_VERIFY(vkCreateSwapchainKHR(m_ctx.GetDevice(), &sci, nullptr, &m_swapchain), "vkCreateSwapchainKHR");
|
||||||
|
|
||||||
|
Uint32 actualCount = 0;
|
||||||
|
vkGetSwapchainImagesKHR(m_ctx.GetDevice(), m_swapchain, &actualCount, nullptr);
|
||||||
|
m_images.resize(actualCount);
|
||||||
|
vkGetSwapchainImagesKHR(m_ctx.GetDevice(), m_swapchain, &actualCount, m_images.data());
|
||||||
|
|
||||||
|
m_imageViews.clear();
|
||||||
|
m_imageViews.reserve(actualCount);
|
||||||
|
for (auto image : m_images) {
|
||||||
|
VkImageViewCreateInfo ivci{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
|
||||||
|
ivci.image = image;
|
||||||
|
ivci.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||||
|
ivci.format = surfaceFormat.format;
|
||||||
|
ivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
ivci.subresourceRange.baseMipLevel = 0;
|
||||||
|
ivci.subresourceRange.levelCount = 1;
|
||||||
|
ivci.subresourceRange.baseArrayLayer = 0;
|
||||||
|
ivci.subresourceRange.layerCount = 1;
|
||||||
|
VkImageView view = VK_NULL_HANDLE;
|
||||||
|
VK_VERIFY(vkCreateImageView(m_ctx.GetDevice(), &ivci, nullptr, &view), "vkCreateImageView swapchain");
|
||||||
|
m_imageViews.push_back(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_imagesInFlight.assign(actualCount, VK_NULL_HANDLE);
|
||||||
|
m_format = surfaceFormat.format;
|
||||||
|
m_extent = extent;
|
||||||
|
}
|
||||||
|
} // namespace MobileGL::MG_Backend::DirectVulkanTMP::VkManager
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Backend/DirectVulkanTMP/Renderer/SwapchainManager.h
|
||||||
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||||
|
// Licensed under the GNU Lesser General Public License v3.0:
|
||||||
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only
|
||||||
|
// End of Source File Header
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <Includes.h>
|
||||||
|
#include "MG_Util/Math/VectorTypes.h"
|
||||||
|
#include "VulkanContext.h"
|
||||||
|
#include "VkCommon.h"
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Backend::DirectVulkanTMP::VkManager {
|
||||||
|
class SwapchainManager {
|
||||||
|
public:
|
||||||
|
explicit SwapchainManager(VulkanContext& ctx) : m_ctx(ctx) {}
|
||||||
|
~SwapchainManager();
|
||||||
|
|
||||||
|
SwapchainManager(const SwapchainManager&) = delete;
|
||||||
|
SwapchainManager& operator=(const SwapchainManager&) = delete;
|
||||||
|
|
||||||
|
void Initialize();
|
||||||
|
void Recreate();
|
||||||
|
void SetViewportSize(const UintVec2& size) { m_viewportSize = size; }
|
||||||
|
|
||||||
|
VkSwapchainKHR GetSwapchain() const { return m_swapchain; }
|
||||||
|
VkFormat GetFormat() const { return m_format; }
|
||||||
|
VkExtent2D GetExtent() const { return m_extent; }
|
||||||
|
const Vector<VkImage>& GetImages() const { return m_images; }
|
||||||
|
const Vector<VkImageView>& GetImageViews() const { return m_imageViews; }
|
||||||
|
const Vector<VkFramebuffer>& GetFramebuffers() const { return m_framebuffers; }
|
||||||
|
void SetFramebuffers(Vector<VkFramebuffer>&& framebuffers);
|
||||||
|
Vector<VkFence>& GetImagesInFlight() { return m_imagesInFlight; }
|
||||||
|
const UintVec2& GetViewportSize() const { return m_viewportSize; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void CreateSwapchain(VkSwapchainKHR oldSwapchain);
|
||||||
|
void DestroySwapchain();
|
||||||
|
|
||||||
|
VulkanContext& m_ctx;
|
||||||
|
VkSwapchainKHR m_swapchain = VK_NULL_HANDLE;
|
||||||
|
VkFormat m_format = VK_FORMAT_UNDEFINED;
|
||||||
|
VkExtent2D m_extent{0, 0};
|
||||||
|
Vector<VkImage> m_images;
|
||||||
|
Vector<VkImageView> m_imageViews;
|
||||||
|
Vector<VkFramebuffer> m_framebuffers;
|
||||||
|
Vector<VkFence> m_imagesInFlight;
|
||||||
|
UintVec2 m_viewportSize{0, 0};
|
||||||
|
};
|
||||||
|
} // namespace MobileGL::MG_Backend::DirectVulkanTMP::VkManager
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Backend/DirectVulkanTMP/Renderer/VkCommon.h
|
||||||
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||||
|
// Licensed under the GNU Lesser General Public License v3.0:
|
||||||
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only
|
||||||
|
// End of Source File Header
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <Includes.h>
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Backend::DirectVulkanTMP::VkManager {
|
||||||
|
inline void VkCheck(VkResult result, const char* msg) {
|
||||||
|
if (result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) return;
|
||||||
|
MGLOG_E("Vulkan error %d at %s", static_cast<Int>(result), msg ? msg : "(unknown)");
|
||||||
|
throw RuntimeError(msg ? msg : "Vulkan error");
|
||||||
|
}
|
||||||
|
} // namespace MobileGL::MG_Backend::DirectVulkanTMP::VkManager
|
||||||
|
|
||||||
|
#ifndef VK_VERIFY
|
||||||
|
#define VK_VERIFY(res, msg) ::MobileGL::MG_Backend::DirectVulkanTMP::VkManager::VkCheck((res), (msg))
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Backend/DirectVulkanTMP/Renderer/VulkanContext.cpp
|
||||||
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||||
|
// Licensed under the GNU Lesser General Public License v3.0:
|
||||||
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only
|
||||||
|
// End of Source File Header
|
||||||
|
|
||||||
|
#include "VulkanContext.h"
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Backend::DirectVulkanTMP::VkManager {
|
||||||
|
namespace {
|
||||||
|
Bool CheckDeviceExtensionSupport(VkPhysicalDevice device, const Vector<const char*>& required) {
|
||||||
|
Uint32 count = 0;
|
||||||
|
vkEnumerateDeviceExtensionProperties(device, nullptr, &count, nullptr);
|
||||||
|
Vector<VkExtensionProperties> props(count);
|
||||||
|
if (count > 0) vkEnumerateDeviceExtensionProperties(device, nullptr, &count, props.data());
|
||||||
|
|
||||||
|
for (auto* ext : required) {
|
||||||
|
Bool found = false;
|
||||||
|
for (const auto& p : props) {
|
||||||
|
if (std::strcmp(p.extensionName, ext) == 0) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool FindGraphicsQueueFamily(VkPhysicalDevice device, VkSurfaceKHR surface, Uint32& outFamily) {
|
||||||
|
Uint32 count = 0;
|
||||||
|
vkGetPhysicalDeviceQueueFamilyProperties(device, &count, nullptr);
|
||||||
|
if (count == 0) return false;
|
||||||
|
Vector<VkQueueFamilyProperties> props(count);
|
||||||
|
vkGetPhysicalDeviceQueueFamilyProperties(device, &count, props.data());
|
||||||
|
|
||||||
|
for (Uint32 i = 0; i < count; ++i) {
|
||||||
|
if (!(props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)) continue;
|
||||||
|
if (surface != VK_NULL_HANDLE) {
|
||||||
|
VkBool32 presentSupport = VK_FALSE;
|
||||||
|
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
|
||||||
|
if (!presentSupport) continue;
|
||||||
|
}
|
||||||
|
outFamily = i;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool SupportsDynamicRendering(VkPhysicalDevice device) {
|
||||||
|
VkPhysicalDeviceVulkan13Features vk13{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES};
|
||||||
|
VkPhysicalDeviceFeatures2 features2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2};
|
||||||
|
features2.pNext = &vk13;
|
||||||
|
vkGetPhysicalDeviceFeatures2(device, &features2);
|
||||||
|
return vk13.dynamicRendering == VK_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool SupportsTimelineSemaphore(VkPhysicalDevice device) {
|
||||||
|
VkPhysicalDeviceVulkan12Features vk12{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES};
|
||||||
|
VkPhysicalDeviceFeatures2 features2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2};
|
||||||
|
features2.pNext = &vk12;
|
||||||
|
vkGetPhysicalDeviceFeatures2(device, &features2);
|
||||||
|
return vk12.timelineSemaphore == VK_TRUE;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
VulkanContext::~VulkanContext() {
|
||||||
|
Cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanContext::Initialize(ANativeWindow* window, const char* appName) {
|
||||||
|
if (m_instance != VK_NULL_HANDLE) return;
|
||||||
|
m_window = window;
|
||||||
|
CreateInstance(appName ? appName : "MobileGL-Vulkan");
|
||||||
|
CreateSurface(window);
|
||||||
|
PickPhysicalDevice();
|
||||||
|
CreateDevice();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanContext::Cleanup() {
|
||||||
|
if (m_device != VK_NULL_HANDLE) {
|
||||||
|
vkDeviceWaitIdle(m_device);
|
||||||
|
vkDestroyDevice(m_device, nullptr);
|
||||||
|
m_device = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
if (m_surface != VK_NULL_HANDLE) {
|
||||||
|
vkDestroySurfaceKHR(m_instance, m_surface, nullptr);
|
||||||
|
m_surface = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
if (m_instance != VK_NULL_HANDLE) {
|
||||||
|
vkDestroyInstance(m_instance, nullptr);
|
||||||
|
m_instance = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
m_physicalDevice = VK_NULL_HANDLE;
|
||||||
|
m_graphicsQueue = VK_NULL_HANDLE;
|
||||||
|
m_graphicsQueueFamily = ~0u;
|
||||||
|
m_window = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanContext::CreateInstance(const char* appName) {
|
||||||
|
VkApplicationInfo app{VK_STRUCTURE_TYPE_APPLICATION_INFO};
|
||||||
|
app.pApplicationName = appName;
|
||||||
|
app.applicationVersion = VK_MAKE_VERSION(1, 3, 0);
|
||||||
|
app.pEngineName = "MobileGL";
|
||||||
|
app.engineVersion = VK_MAKE_VERSION(1, 3, 0);
|
||||||
|
app.apiVersion = VK_API_VERSION_1_3;
|
||||||
|
|
||||||
|
Vector<const char*> extensions;
|
||||||
|
extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
|
||||||
|
#ifdef VK_USE_PLATFORM_ANDROID_KHR
|
||||||
|
extensions.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
VkInstanceCreateInfo ici{VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO};
|
||||||
|
ici.pApplicationInfo = &app;
|
||||||
|
ici.enabledExtensionCount = static_cast<Uint32>(extensions.size());
|
||||||
|
ici.ppEnabledExtensionNames = extensions.data();
|
||||||
|
|
||||||
|
VK_VERIFY(vkCreateInstance(&ici, nullptr, &m_instance), "vkCreateInstance");
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanContext::CreateSurface(ANativeWindow* window) {
|
||||||
|
if (!window) return;
|
||||||
|
#ifdef VK_USE_PLATFORM_ANDROID_KHR
|
||||||
|
VkAndroidSurfaceCreateInfoKHR sci{VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR};
|
||||||
|
sci.window = window;
|
||||||
|
VK_VERIFY(vkCreateAndroidSurfaceKHR(m_instance, &sci, nullptr, &m_surface), "vkCreateAndroidSurfaceKHR");
|
||||||
|
#else
|
||||||
|
(void)window;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanContext::PickPhysicalDevice() {
|
||||||
|
Uint32 count = 0;
|
||||||
|
VK_VERIFY(vkEnumeratePhysicalDevices(m_instance, &count, nullptr), "vkEnumeratePhysicalDevices");
|
||||||
|
if (count == 0) throw RuntimeError("No Vulkan physical devices found");
|
||||||
|
|
||||||
|
Vector<VkPhysicalDevice> devices(count);
|
||||||
|
VK_VERIFY(vkEnumeratePhysicalDevices(m_instance, &count, devices.data()), "vkEnumeratePhysicalDevices list");
|
||||||
|
|
||||||
|
Vector<const char*> requiredExts = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
|
||||||
|
|
||||||
|
for (auto device : devices) {
|
||||||
|
Uint32 family = ~0u;
|
||||||
|
if (!FindGraphicsQueueFamily(device, m_surface, family)) continue;
|
||||||
|
if (!CheckDeviceExtensionSupport(device, requiredExts)) continue;
|
||||||
|
if (!SupportsDynamicRendering(device)) continue;
|
||||||
|
|
||||||
|
m_physicalDevice = device;
|
||||||
|
m_graphicsQueueFamily = family;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_physicalDevice == VK_NULL_HANDLE) {
|
||||||
|
throw RuntimeError("No suitable Vulkan physical device found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanContext::CreateDevice() {
|
||||||
|
float priority = 1.0f;
|
||||||
|
VkDeviceQueueCreateInfo qci{VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO};
|
||||||
|
qci.queueFamilyIndex = m_graphicsQueueFamily;
|
||||||
|
qci.queueCount = 1;
|
||||||
|
qci.pQueuePriorities = &priority;
|
||||||
|
|
||||||
|
Vector<const char*> deviceExtensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
|
||||||
|
|
||||||
|
VkPhysicalDeviceFeatures features{};
|
||||||
|
VkPhysicalDeviceVulkan13Features vk13{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES};
|
||||||
|
vk13.dynamicRendering = VK_TRUE;
|
||||||
|
VkPhysicalDeviceVulkan12Features vk12{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES};
|
||||||
|
vk12.timelineSemaphore = SupportsTimelineSemaphore(m_physicalDevice) ? VK_TRUE : VK_FALSE;
|
||||||
|
VkPhysicalDeviceFeatures2 features2{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2};
|
||||||
|
features2.features = features;
|
||||||
|
vk13.pNext = &vk12;
|
||||||
|
features2.pNext = &vk13;
|
||||||
|
|
||||||
|
VkDeviceCreateInfo dci{VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO};
|
||||||
|
dci.pNext = &features2;
|
||||||
|
dci.queueCreateInfoCount = 1;
|
||||||
|
dci.pQueueCreateInfos = &qci;
|
||||||
|
dci.enabledExtensionCount = static_cast<Uint32>(deviceExtensions.size());
|
||||||
|
dci.ppEnabledExtensionNames = deviceExtensions.data();
|
||||||
|
dci.pEnabledFeatures = nullptr;
|
||||||
|
|
||||||
|
VK_VERIFY(vkCreateDevice(m_physicalDevice, &dci, nullptr, &m_device), "vkCreateDevice");
|
||||||
|
vkGetDeviceQueue(m_device, m_graphicsQueueFamily, 0, &m_graphicsQueue);
|
||||||
|
}
|
||||||
|
} // namespace MobileGL::MG_Backend::DirectVulkanTMP::VkManager
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Backend/DirectVulkanTMP/Renderer/VulkanContext.h
|
||||||
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||||
|
// Licensed under the GNU Lesser General Public License v3.0:
|
||||||
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only
|
||||||
|
// End of Source File Header
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <Includes.h>
|
||||||
|
#include "VkCommon.h"
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Backend::DirectVulkanTMP::VkManager {
|
||||||
|
class VulkanContext {
|
||||||
|
public:
|
||||||
|
VulkanContext() = default;
|
||||||
|
~VulkanContext();
|
||||||
|
|
||||||
|
VulkanContext(const VulkanContext&) = delete;
|
||||||
|
VulkanContext& operator=(const VulkanContext&) = delete;
|
||||||
|
|
||||||
|
void Initialize(ANativeWindow* window, const char* appName);
|
||||||
|
void Cleanup();
|
||||||
|
|
||||||
|
VkInstance GetInstance() const { return m_instance; }
|
||||||
|
VkPhysicalDevice GetPhysicalDevice() const { return m_physicalDevice; }
|
||||||
|
VkDevice GetDevice() const { return m_device; }
|
||||||
|
VkQueue GetGraphicsQueue() const { return m_graphicsQueue; }
|
||||||
|
Uint32 GetGraphicsQueueFamily() const { return m_graphicsQueueFamily; }
|
||||||
|
VkSurfaceKHR GetSurface() const { return m_surface; }
|
||||||
|
ANativeWindow* GetWindow() const { return m_window; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void CreateInstance(const char* appName);
|
||||||
|
void CreateSurface(ANativeWindow* window);
|
||||||
|
void PickPhysicalDevice();
|
||||||
|
void CreateDevice();
|
||||||
|
|
||||||
|
VkInstance m_instance = VK_NULL_HANDLE;
|
||||||
|
VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE;
|
||||||
|
VkDevice m_device = VK_NULL_HANDLE;
|
||||||
|
VkQueue m_graphicsQueue = VK_NULL_HANDLE;
|
||||||
|
VkSurfaceKHR m_surface = VK_NULL_HANDLE;
|
||||||
|
Uint32 m_graphicsQueueFamily = ~0u;
|
||||||
|
ANativeWindow* m_window = nullptr;
|
||||||
|
};
|
||||||
|
} // namespace MobileGL::MG_Backend::DirectVulkanTMP::VkManager
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,308 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Backend/DirectVulkanTMP/TmpImpl.h
|
||||||
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||||
|
// Licensed under the GNU Lesser General Public License v3.0:
|
||||||
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only
|
||||||
|
// End of Source File Header
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <Includes.h>
|
||||||
|
#include "Renderer/VkCommon.h"
|
||||||
|
#include "Renderer/VulkanContext.h"
|
||||||
|
#include "Renderer/SwapchainManager.h"
|
||||||
|
#include "Renderer/FrameContext.h"
|
||||||
|
#include "Managers/ProgramManager.h"
|
||||||
|
#include <MG_State/GLState/Core.h>
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Backend::DirectVulkanTMP::TmpImpl {
|
||||||
|
namespace DV = MobileGL::MG_Backend::DirectVulkanTMP::VkManager;
|
||||||
|
using BufferObject = MobileGL::MG_State::GLState::BufferObject;
|
||||||
|
using ProgramObject = MobileGL::MG_State::GLState::ProgramObject;
|
||||||
|
using VertexArrayObject = MobileGL::MG_State::GLState::VertexArrayObject;
|
||||||
|
using FramebufferObject = MobileGL::MG_State::GLState::FramebufferObject;
|
||||||
|
using FramebufferAttachmentObject = MobileGL::MG_State::GLState::FramebufferAttachmentObject;
|
||||||
|
using ITextureObject = MobileGL::MG_State::GLState::ITextureObject;
|
||||||
|
using RenderbufferObject = MobileGL::MG_State::GLState::RenderbufferObject;
|
||||||
|
using SamplerObject = MobileGL::MG_State::GLState::SamplerObject;
|
||||||
|
using TextureObjectMipmap = MobileGL::MG_State::GLState::TextureObjectMipmap;
|
||||||
|
using TrashImage = MobileGL::MG_Backend::DirectVulkanTMP::TrashImage;
|
||||||
|
|
||||||
|
struct BufferResource {
|
||||||
|
VkBuffer buffer = VK_NULL_HANDLE;
|
||||||
|
VkDeviceMemory memory = VK_NULL_HANDLE;
|
||||||
|
VkDeviceSize size = 0;
|
||||||
|
VkDeviceSize offset = 0;
|
||||||
|
VkBufferUsageFlags usage = 0;
|
||||||
|
VkMemoryPropertyFlags props = 0;
|
||||||
|
void* mapped = nullptr;
|
||||||
|
Bool fromRing = false;
|
||||||
|
Uint32 lastUsedFrame = ~0u;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BufferResourceSet {
|
||||||
|
Vector<BufferResource> perFrame;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PendingBufferCopyBatch {
|
||||||
|
VkBuffer src = VK_NULL_HANDLE;
|
||||||
|
VkBuffer dst = VK_NULL_HANDLE;
|
||||||
|
Vector<VkBufferCopy> regions;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StagingRing {
|
||||||
|
BufferResource buffer;
|
||||||
|
VkDeviceSize capacity = 0;
|
||||||
|
VkDeviceSize head = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TextureResource {
|
||||||
|
VkImage image = VK_NULL_HANDLE;
|
||||||
|
VkDeviceMemory memory = VK_NULL_HANDLE;
|
||||||
|
VkImageView view = VK_NULL_HANDLE;
|
||||||
|
UnorderedMap<Uint32, VkImageView> mipViews;
|
||||||
|
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||||
|
VkExtent3D extent{0, 0, 1};
|
||||||
|
VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
Uint32 mipLevels = 1;
|
||||||
|
Uint16 paramsVersion = 0;
|
||||||
|
bool valid = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RenderbufferResource {
|
||||||
|
VkImage image = VK_NULL_HANDLE;
|
||||||
|
VkDeviceMemory memory = VK_NULL_HANDLE;
|
||||||
|
VkImageView view = VK_NULL_HANDLE;
|
||||||
|
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||||
|
VkExtent2D extent{0, 0};
|
||||||
|
VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
bool valid = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SamplerResource {
|
||||||
|
VkSampler sampler = VK_NULL_HANDLE;
|
||||||
|
Uint16 version = 0;
|
||||||
|
SamplerParameters params{};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PendingClearInfo {
|
||||||
|
GLbitfield mask = 0;
|
||||||
|
FloatVec4 clearColor = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||||
|
Float clearDepth = 1.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UniformBindingInfo {
|
||||||
|
String name;
|
||||||
|
Uint32 binding = 0;
|
||||||
|
Uint32 set = 0;
|
||||||
|
Uint32 blockIndex = 0xFFFFFFFFu;
|
||||||
|
VkShaderStageFlags stages = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SamplerBindingInfo {
|
||||||
|
String name;
|
||||||
|
Uint32 binding = 0;
|
||||||
|
Uint32 set = 0;
|
||||||
|
VkShaderStageFlags stages = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ProgramResource {
|
||||||
|
ProgramObject* program = nullptr;
|
||||||
|
Uint64 spvHash = 0;
|
||||||
|
DV::ProgramManager::HashType programHash = 0;
|
||||||
|
Vector<VkPipelineShaderStageCreateInfo>* shaderStages = nullptr;
|
||||||
|
|
||||||
|
VkDescriptorSetLayout setLayout = VK_NULL_HANDLE;
|
||||||
|
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
struct UboPool {
|
||||||
|
Vector<BufferResource> buffers;
|
||||||
|
Uint32 cursor = 0;
|
||||||
|
};
|
||||||
|
Vector<UboPool> uboPools;
|
||||||
|
SizeT uboSize = 0;
|
||||||
|
Int32 uboBinding = -1;
|
||||||
|
Uint32 uniformDescriptorCount = 0;
|
||||||
|
Uint32 samplerDescriptorCount = 0;
|
||||||
|
|
||||||
|
Vector<UniformBindingInfo> uniformBindings;
|
||||||
|
Vector<SamplerBindingInfo> samplerBindings;
|
||||||
|
UnorderedMap<String, Uint32> samplerBindingByName;
|
||||||
|
|
||||||
|
UnorderedMap<Uint64, VkPipeline> pipelines;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DescriptorPoolConfig {
|
||||||
|
Uint32 maxSets = 0;
|
||||||
|
Uint32 uniformCount = 0;
|
||||||
|
Uint32 samplerCount = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FrameDescriptorPools {
|
||||||
|
Vector<VkDescriptorPool> pools;
|
||||||
|
DescriptorPoolConfig config;
|
||||||
|
Uint32 activePool = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VertexAttribKey {
|
||||||
|
Uint8 enabled = 0;
|
||||||
|
Uint8 size = 0;
|
||||||
|
Uint8 type = 0;
|
||||||
|
Uint8 normalized = 0;
|
||||||
|
Uint8 isInteger = 0;
|
||||||
|
Uint8 divisor = 0;
|
||||||
|
Uint16 pad = 0;
|
||||||
|
Uint32 stride = 0;
|
||||||
|
Uint64 offset = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AttachmentInfo {
|
||||||
|
FramebufferAttachmentType type = FramebufferAttachmentType::None;
|
||||||
|
VkImageView view = VK_NULL_HANDLE;
|
||||||
|
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||||
|
VkImageAspectFlags aspect = 0;
|
||||||
|
VkImageLayout finalLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
ITextureObject* texture = nullptr;
|
||||||
|
RenderbufferObject* renderbuffer = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BackendFramebufferObject {
|
||||||
|
VkExtent2D extent{0, 0};
|
||||||
|
Uint32 colorAttachmentCount = 0;
|
||||||
|
Bool hasDepth = false;
|
||||||
|
VkFormat depthFormat = VK_FORMAT_UNDEFINED;
|
||||||
|
VkFormat depthAttachmentFormat = VK_FORMAT_UNDEFINED;
|
||||||
|
VkFormat stencilAttachmentFormat = VK_FORMAT_UNDEFINED;
|
||||||
|
Uint64 renderingCompatHash = 0;
|
||||||
|
|
||||||
|
Vector<AttachmentInfo> attachments;
|
||||||
|
Vector<VkFormat> colorAttachmentFormats;
|
||||||
|
Array<Int32, static_cast<SizeT>(FramebufferAttachmentType::FramebufferAttachmentTypeCount)> attachmentIndex =
|
||||||
|
{};
|
||||||
|
|
||||||
|
FramebufferObject::FramebufferAttachmentVersionArray syncedAttachmentVersions = {0};
|
||||||
|
FramebufferObject::FramebufferAttachmentArray frontendDrawBuffers = {FramebufferAttachmentType::None};
|
||||||
|
Array<Int32, FramebufferObject::MAX_DRAW_BUFFERS> drawBufferAttachmentIndices = {};
|
||||||
|
FramebufferAttachmentType frontendReadBuffer = FramebufferAttachmentType::Color0;
|
||||||
|
Uint64 configHash = 0;
|
||||||
|
Uint16 objectVersion = 0;
|
||||||
|
|
||||||
|
Bool IsValid() const { return !attachments.empty() && extent.width > 0 && extent.height > 0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VulkanState {
|
||||||
|
UniquePtr<DV::VulkanContext> ctx;
|
||||||
|
UniquePtr<DV::SwapchainManager> swapchain;
|
||||||
|
UniquePtr<DV::ProgramManager> programMgr;
|
||||||
|
VkCommandPool commandPool = VK_NULL_HANDLE;
|
||||||
|
Vector<UniquePtr<DV::FrameContext>> frames;
|
||||||
|
Vector<FrameDescriptorPools> frameDescriptorPools;
|
||||||
|
Vector<BufferResource> stagingPool;
|
||||||
|
Vector<Vector<BufferResource>> stagingPending;
|
||||||
|
Uint32 currentFrame = 0;
|
||||||
|
Uint32 maxFramesInFlight = 2;
|
||||||
|
Bool initialized = false;
|
||||||
|
UintVec2 viewportSize{0, 0};
|
||||||
|
|
||||||
|
VkImage depthImage = VK_NULL_HANDLE;
|
||||||
|
VkDeviceMemory depthMemory = VK_NULL_HANDLE;
|
||||||
|
VkImageView depthView = VK_NULL_HANDLE;
|
||||||
|
VkFormat depthFormat = VK_FORMAT_UNDEFINED;
|
||||||
|
Uint64 defaultRenderingCompatHash = 0;
|
||||||
|
|
||||||
|
BufferResource nullUbo;
|
||||||
|
|
||||||
|
TextureResource defaultTexture;
|
||||||
|
SamplerResource defaultSampler;
|
||||||
|
|
||||||
|
UnorderedMap<const BufferObject*, BufferResourceSet> buffers;
|
||||||
|
UnorderedMap<const ITextureObject*, TextureResource> textures;
|
||||||
|
UnorderedMap<const RenderbufferObject*, RenderbufferResource> renderbuffers;
|
||||||
|
UnorderedMap<const SamplerObject*, SamplerResource> samplers;
|
||||||
|
UnorderedMap<const ProgramObject*, ProgramResource> programs;
|
||||||
|
UnorderedMap<SharedPtr<MG_State::GLState::FramebufferObject>, SharedPtr<BackendFramebufferObject>> framebuffers;
|
||||||
|
Vector<PendingBufferCopyBatch> pendingBufferCopies;
|
||||||
|
Vector<StagingRing> stagingRings;
|
||||||
|
|
||||||
|
VkExtent2D activeExtent{0, 0};
|
||||||
|
Vector<VkImageView> activeColorViews;
|
||||||
|
Vector<VkImageLayout> activeColorLayouts;
|
||||||
|
Vector<VkFormat> activeColorFormats;
|
||||||
|
Vector<Int32> activeColorAttachmentIndices;
|
||||||
|
VkImageView activeDepthView = VK_NULL_HANDLE;
|
||||||
|
VkImageLayout activeDepthLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
VkImageView activeStencilView = VK_NULL_HANDLE;
|
||||||
|
VkImageLayout activeStencilLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
Uint32 activeSwapchainImageIndex = ~0u;
|
||||||
|
Uint32 activeColorAttachmentCount = 1;
|
||||||
|
Bool activeHasDepth = false;
|
||||||
|
VkFormat activeDepthFormat = VK_FORMAT_UNDEFINED;
|
||||||
|
VkFormat activeDepthAttachmentFormat = VK_FORMAT_UNDEFINED;
|
||||||
|
VkFormat activeStencilAttachmentFormat = VK_FORMAT_UNDEFINED;
|
||||||
|
Uint64 activeRenderingCompatHash = 0;
|
||||||
|
Uint64 activeRenderingConfigHash = 0;
|
||||||
|
SharedPtr<MG_State::GLState::FramebufferObject> activeStateFBO = nullptr;
|
||||||
|
SharedPtr<BackendFramebufferObject> activeBackendFBO = nullptr;
|
||||||
|
Bool activeIsDefault = true;
|
||||||
|
Bool activeDefaultColorWrites = true;
|
||||||
|
|
||||||
|
Uint64 recordingRenderingConfigHash = 0;
|
||||||
|
Bool cmdBufferBegun = false;
|
||||||
|
|
||||||
|
Bool recording = false;
|
||||||
|
Bool frameSubmitted = false;
|
||||||
|
FloatVec4 clearColor = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||||
|
Float clearDepth = 1.0f;
|
||||||
|
|
||||||
|
UnorderedMap<SharedPtr<FramebufferObject>, PendingClearInfo> pendingClears;
|
||||||
|
|
||||||
|
Vector<VkImageLayout> swapchainImageLayouts;
|
||||||
|
|
||||||
|
PFN_vkCmdBeginRendering pfnCmdBeginRendering = nullptr;
|
||||||
|
PFN_vkCmdEndRendering pfnCmdEndRendering = nullptr;
|
||||||
|
PFN_vkWaitSemaphores pfnWaitSemaphores = nullptr;
|
||||||
|
VkSemaphore timelineSemaphore = VK_NULL_HANDLE;
|
||||||
|
Uint64 timelineValue = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void Present();
|
||||||
|
void FrameBegin();
|
||||||
|
void InitVulkan(ANativeWindow* window);
|
||||||
|
const VulkanState& GetVulkanState();
|
||||||
|
void ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
|
||||||
|
void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value);
|
||||||
|
void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value);
|
||||||
|
void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value);
|
||||||
|
void Clear(GLbitfield mask);
|
||||||
|
void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices);
|
||||||
|
void DrawArrays(GLenum mode, GLint first, GLsizei count);
|
||||||
|
void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices, GLint basevertex);
|
||||||
|
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
|
||||||
|
GLsizei drawcount);
|
||||||
|
void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
|
||||||
|
GLsizei drawcount, const GLint* basevertex);
|
||||||
|
void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride);
|
||||||
|
void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride);
|
||||||
|
void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
|
||||||
|
const void* indices, GLint basevertex);
|
||||||
|
void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices);
|
||||||
|
void DrawElementsInstancedBaseVertexBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices,
|
||||||
|
GLsizei instancecount, GLint basevertex, GLuint baseinstance);
|
||||||
|
void DrawElementsInstancedBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices,
|
||||||
|
GLsizei instancecount, GLint basevertex);
|
||||||
|
void DrawElementsInstancedBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices,
|
||||||
|
GLsizei instancecount, GLuint baseinstance);
|
||||||
|
void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount);
|
||||||
|
void DrawElementsIndirect(GLenum mode, GLenum type, const void* indirect);
|
||||||
|
void DrawArraysInstancedBaseInstance(GLenum mode, GLint first, GLsizei count, GLsizei instancecount,
|
||||||
|
GLuint baseinstance);
|
||||||
|
void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount);
|
||||||
|
void DrawArraysIndirect(GLenum mode, const void* indirect);
|
||||||
|
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1,
|
||||||
|
GLint dstY1, GLbitfield mask, GLenum filter);
|
||||||
|
void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width,
|
||||||
|
GLsizei height, GLint border);
|
||||||
|
void CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width,
|
||||||
|
GLsizei height);
|
||||||
|
void GenerateMipmap(GLenum target);
|
||||||
|
} // namespace MobileGL::MG_Backend::DirectVulkanTMP::TmpImpl
|
||||||
@@ -52,6 +52,9 @@ namespace MobileGL::MG_Backend {
|
|||||||
case BackendType::DirectGLES:
|
case BackendType::DirectGLES:
|
||||||
pActiveBackendObject = MakeUnique<DirectGLES::BackendObject_DirectGLES>();
|
pActiveBackendObject = MakeUnique<DirectGLES::BackendObject_DirectGLES>();
|
||||||
break;
|
break;
|
||||||
|
case BackendType::DirectVulkanTMP:
|
||||||
|
pActiveBackendObject = MakeUnique<DirectVulkanTMP::BackendObject_DirectVulkanTMP>();
|
||||||
|
break;
|
||||||
case BackendType::Unknown:
|
case BackendType::Unknown:
|
||||||
default:
|
default:
|
||||||
MGLOG_W("Unknown backend type, defaulting to unknown backend");
|
MGLOG_W("Unknown backend type, defaulting to unknown backend");
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
// End of Source File Header
|
// End of Source File Header
|
||||||
|
|
||||||
#include "GL_Getter.h"
|
#include "GL_Getter.h"
|
||||||
#include "GL/gl.h"
|
|
||||||
#include "MG_Util/Debug/Log.h"
|
|
||||||
#include <Config.h>
|
#include <Config.h>
|
||||||
#include <MGGitHash.h>
|
#include <MGGitHash.h>
|
||||||
#include <MG_State/GLState/Core.h>
|
#include <MG_State/GLState/Core.h>
|
||||||
|
|||||||
@@ -110,6 +110,11 @@ namespace MobileGL {
|
|||||||
AddDefaultFragmentShaderIfMissing();
|
AddDefaultFragmentShaderIfMissing();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::sort(m_shaders.begin(), m_shaders.end(),
|
||||||
|
[](SharedPtr<ShaderObject>& a, SharedPtr<ShaderObject>& b) {
|
||||||
|
return a->GetShaderStage() < b->GetShaderStage();
|
||||||
|
});
|
||||||
|
|
||||||
for (SizeT i = 0; i < m_shaders.size(); i++) {
|
for (SizeT i = 0; i < m_shaders.size(); i++) {
|
||||||
shaderTypes[i] = MG_Util::ConvertShaderStageToGLEnum(m_shaders[i]->GetShaderStage());
|
shaderTypes[i] = MG_Util::ConvertShaderStageToGLEnum(m_shaders[i]->GetShaderStage());
|
||||||
MGLOG_D("ProgramObject %u: Preparing shader[%zu] stage %s at %p", m_externalIndex, i,
|
MGLOG_D("ProgramObject %u: Preparing shader[%zu] stage %s at %p", m_externalIndex, i,
|
||||||
|
|||||||
@@ -112,6 +112,13 @@ namespace MobileGL {
|
|||||||
|
|
||||||
Vector<Vector<unsigned>>& GetGeneratedSpirv() { return m_generatedSpirv; }
|
Vector<Vector<unsigned>>& GetGeneratedSpirv() { return m_generatedSpirv; }
|
||||||
|
|
||||||
|
Int GetShaderIndexByStage(ShaderStage stage) const {
|
||||||
|
auto it = std::find_if(
|
||||||
|
m_shaders.begin(), m_shaders.end(),
|
||||||
|
[stage](const SharedPtr<ShaderObject>& shader) { return shader->GetShaderStage() == stage; });
|
||||||
|
return it == m_shaders.end() ? -1 : std::distance(m_shaders.begin(), it);
|
||||||
|
}
|
||||||
|
|
||||||
Uint GetExternalIndex() const { return m_externalIndex; }
|
Uint GetExternalIndex() const { return m_externalIndex; }
|
||||||
|
|
||||||
// const UnorderedMap<String, Uint>& GetAttribLocationMap() const { return
|
// const UnorderedMap<String, Uint>& GetAttribLocationMap() const { return
|
||||||
|
|||||||
@@ -53,4 +53,4 @@ namespace MobileGL {
|
|||||||
}
|
}
|
||||||
} // namespace GLState
|
} // namespace GLState
|
||||||
} // namespace MG_State
|
} // namespace MG_State
|
||||||
} // namespace MobileGL
|
} // namespace MobileGL
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ namespace MobileGL {
|
|||||||
Geometry,
|
Geometry,
|
||||||
Fragment,
|
Fragment,
|
||||||
Compute,
|
Compute,
|
||||||
|
ShaderStageCount,
|
||||||
Unknown = -1
|
Unknown = -1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -983,7 +983,7 @@ TEST_F(ProgramTest, CompileAndLinkWithExplicitFragmentOut) {
|
|||||||
|
|
||||||
auto programObject = MG_State::pGLContext->GetCurrentProgram();
|
auto programObject = MG_State::pGLContext->GetCurrentProgram();
|
||||||
auto& spirvs = programObject->GetGeneratedSpirv();
|
auto& spirvs = programObject->GetGeneratedSpirv();
|
||||||
auto& fragSpirv = spirvs[0]; // 0 - fragment, 1 - vertex
|
auto& fragSpirv = spirvs[programObject->GetShaderIndexByStage(ShaderStage::Fragment)];
|
||||||
char* pSrcfragOut = nullptr;
|
char* pSrcfragOut = nullptr;
|
||||||
const char* needle = "layout(location = 7) out vec4 fragColor;";
|
const char* needle = "layout(location = 7) out vec4 fragColor;";
|
||||||
// for (auto spirv: spirvs) {
|
// for (auto spirv: spirvs) {
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp
|
||||||
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||||
|
// Licensed under the GNU Lesser General Public License v3.0:
|
||||||
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only
|
||||||
|
// End of Source File Header
|
||||||
|
|
||||||
|
#include "Loader.h"
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Util::BackendLoader {
|
||||||
|
inline Version DecodeApiVersion(uint32_t version) {
|
||||||
|
return {(Int)VK_VERSION_MAJOR(version), (Int)VK_VERSION_MINOR(version), (Int)VK_VERSION_PATCH(version)};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string DecodeDriverVersion(uint32_t driverVersion) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << VK_VERSION_MAJOR(driverVersion) << "." << VK_VERSION_MINOR(driverVersion) << "."
|
||||||
|
<< VK_VERSION_PATCH(driverVersion);
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool QueryVulkanCapabilities(MobileGL::MG_External::VulkanCapabilities& caps, VkPhysicalDevice physicalDevice) {
|
||||||
|
if (!physicalDevice) {
|
||||||
|
MGLOG_E("Invalid physical device handle");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkPhysicalDeviceProperties props{};
|
||||||
|
vkGetPhysicalDeviceProperties(physicalDevice, &props);
|
||||||
|
|
||||||
|
if (props.apiVersion < VK_API_VERSION_1_1) {
|
||||||
|
MGLOG_E("Vulkan API version %u.%u.%u is not supported, requires at least 1.1",
|
||||||
|
VK_VERSION_MAJOR(props.apiVersion), VK_VERSION_MINOR(props.apiVersion),
|
||||||
|
VK_VERSION_PATCH(props.apiVersion));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkPhysicalDeviceProperties2 props2{};
|
||||||
|
props2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
|
||||||
|
vkGetPhysicalDeviceProperties2(physicalDevice, &props2);
|
||||||
|
|
||||||
|
const VkPhysicalDeviceProperties& p = props2.properties;
|
||||||
|
caps.VulkanAPIVersion = DecodeApiVersion(p.apiVersion);
|
||||||
|
caps.DeviceName = p.deviceName;
|
||||||
|
caps.DriverVersionString = DecodeDriverVersion(p.driverVersion);
|
||||||
|
caps.UniformBufferOffsetAlignment = static_cast<int>(p.limits.minUniformBufferOffsetAlignment);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} // namespace MobileGL::MG_Util::BackendLoader
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.h
|
||||||
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||||
|
// Licensed under the GNU Lesser General Public License v3.0:
|
||||||
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only
|
||||||
|
// End of Source File Header
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <Includes.h>
|
||||||
|
|
||||||
|
namespace MobileGL {
|
||||||
|
namespace MG_External {
|
||||||
|
struct VulkanCapabilities {
|
||||||
|
Version VulkanAPIVersion{1, 0, 0};
|
||||||
|
String DeviceName;
|
||||||
|
String DriverVersionString;
|
||||||
|
Int UniformBufferOffsetAlignment = 256;
|
||||||
|
};
|
||||||
|
} // namespace MG_External
|
||||||
|
namespace MG_Util::BackendLoader {
|
||||||
|
Bool QueryVulkanCapabilities(MobileGL::MG_External::VulkanCapabilities& caps, VkPhysicalDevice physicalDevice);
|
||||||
|
} // namespace MG_Util::BackendLoader
|
||||||
|
} // namespace MobileGL
|
||||||
@@ -16,5 +16,6 @@ namespace MobileGL {
|
|||||||
TextureInternalFormat ConvertInternalFormatToSized(TextureInternalFormat internalformat,
|
TextureInternalFormat ConvertInternalFormatToSized(TextureInternalFormat internalformat,
|
||||||
TextureInputFormat format, TexturePixelDataType type);
|
TextureInputFormat format, TexturePixelDataType type);
|
||||||
TextureInternalFormat ConvertInternalFormatToUnsized(TextureInternalFormat internalformat);
|
TextureInternalFormat ConvertInternalFormatToUnsized(TextureInternalFormat internalformat);
|
||||||
|
|
||||||
} // namespace MG_Util
|
} // namespace MG_Util
|
||||||
} // namespace MobileGL
|
} // namespace MobileGL
|
||||||
@@ -14,7 +14,6 @@ namespace MobileGL {
|
|||||||
NoNorm16 = 1 << 0,
|
NoNorm16 = 1 << 0,
|
||||||
None = 0,
|
None = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace MG_Util::TextureFormatProcessor {
|
namespace MG_Util::TextureFormatProcessor {
|
||||||
void NormalizePixelFormat(GLenum internalFormat, Flags<PixelFormatNormalizeOptionBit> options,
|
void NormalizePixelFormat(GLenum internalFormat, Flags<PixelFormatNormalizeOptionBit> options,
|
||||||
GLenum* outInternalFormat, GLenum* outFormat, GLenum* outType);
|
GLenum* outInternalFormat, GLenum* outFormat, GLenum* outType);
|
||||||
|
|||||||
Reference in New Issue
Block a user