Merge branch 'master' into slang_shaders

This commit is contained in:
Sascha Willems 2025-03-19 21:19:28 +01:00
commit 02559cd99a
16 changed files with 71 additions and 75 deletions

View file

@ -48,7 +48,7 @@ jobs:
build_macOS:
name: Build macOS
runs-on: macos-13
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
with:

View file

@ -60,6 +60,11 @@ task copyTask {
include 'metalplate01_rgba.ktx'
}
copy {
from rootProject.ext.assetPath + 'models'
into 'assets/models'
include 'plane_z.gltf'
}
}

View file

@ -3,7 +3,7 @@
*
* Platform specific macros for the example main entry points
*
* Copyright (C) 2024 by Sascha Willems - www.saschawillems.de
* Copyright (C) 2024-2025 by Sascha Willems - www.saschawillems.de
*
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/
@ -42,12 +42,12 @@ int APIENTRY WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance
VulkanExample *vulkanExample; \
void android_main(android_app* state) \
{ \
androidApp = state; \
vks::android::getDeviceConfig(); \
vulkanExample = new VulkanExample(); \
state->userData = vulkanExample; \
state->onAppCmd = VulkanExample::handleAppCommand; \
state->onInputEvent = VulkanExample::handleAppInput; \
androidApp = state; \
vks::android::getDeviceConfig(); \
vulkanExample->renderLoop(); \
delete(vulkanExample); \
}

View file

@ -1,7 +1,7 @@
/*
* Android Vulkan function pointer loader
*
* Copyright (C) 2016-2024 by Sascha Willems - www.saschawillems.de
* Copyright (C) 2016-2025 by Sascha Willems - www.saschawillems.de
*
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/
@ -359,7 +359,6 @@ namespace vks
jni->DeleteLocalRef(jmessage);
androidApp->activity->vm->DetachCurrentThread();
return;
}
}
}

View file

@ -1,7 +1,7 @@
/*
* Android Vulkan function pointer prototypes
*
* Copyright (C) 2016-2024 by Sascha Willems - www.saschawillems.de
* Copyright (C) 2016-2025 by Sascha Willems - www.saschawillems.de
*
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/
@ -29,16 +29,6 @@
#include <memory>
#include <string>
// Missing from the NDK
namespace std
{
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
}
// Global reference to android application object
extern android_app* androidApp;

View file

@ -76,7 +76,7 @@ VkResult VulkanExampleBase::createInstance()
#endif
// Enabled requested instance extensions
if (enabledInstanceExtensions.size() > 0)
if (!enabledInstanceExtensions.empty())
{
for (const char * enabledExtension : enabledInstanceExtensions)
{
@ -120,7 +120,7 @@ VkResult VulkanExampleBase::createInstance()
instanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
if (instanceExtensions.size() > 0) {
if (!instanceExtensions.empty()) {
instanceCreateInfo.enabledExtensionCount = (uint32_t)instanceExtensions.size();
instanceCreateInfo.ppEnabledExtensionNames = instanceExtensions.data();
}
@ -316,7 +316,7 @@ void VulkanExampleBase::renderLoop()
benchmark.run([=] { render(); }, vulkanDevice->properties);
vkDeviceWaitIdle(device);
if (benchmark.filename != "") {
if (!benchmark.filename.empty()) {
benchmark.saveResults();
}
return;
@ -344,7 +344,7 @@ void VulkanExampleBase::renderLoop()
}
}
#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
while (1)
while (true)
{
int ident;
int events;
@ -353,9 +353,9 @@ void VulkanExampleBase::renderLoop()
focused = true;
while ((ident = ALooper_pollOnce(focused ? 0 : -1, NULL, &events, (void**)&source)) > ALOOPER_POLL_TIMEOUT)
while ((ident = ALooper_pollOnce(focused ? 0 : -1, nullptr, &events, (void**)&source)) > ALOOPER_POLL_TIMEOUT)
{
if (source != NULL)
if (source != nullptr)
{
source->process(androidApp, source);
}
@ -404,8 +404,6 @@ void VulkanExampleBase::renderLoop()
updateOverlay();
bool updateView = false;
// Check touch state (for movement)
if (touchDown) {
touchTimer += frameTimer;
@ -422,23 +420,20 @@ void VulkanExampleBase::renderLoop()
if (std::abs(gamePadState.axisLeft.x) > deadZone)
{
camera.rotate(glm::vec3(0.0f, gamePadState.axisLeft.x * 0.5f, 0.0f));
updateView = true;
}
if (std::abs(gamePadState.axisLeft.y) > deadZone)
{
camera.rotate(glm::vec3(gamePadState.axisLeft.y * 0.5f, 0.0f, 0.0f));
updateView = true;
}
// Zoom
if (std::abs(gamePadState.axisRight.y) > deadZone)
{
camera.translate(glm::vec3(0.0f, 0.0f, gamePadState.axisRight.y * 0.01f));
updateView = true;
}
}
else
{
updateView = camera.updatePad(gamePadState.axisLeft, gamePadState.axisRight, frameTimer);
camera.updatePad(gamePadState.axisLeft, gamePadState.axisRight, frameTimer);
}
}
}
@ -915,9 +910,9 @@ VulkanExampleBase::~VulkanExampleBase()
{
vkDestroyRenderPass(device, renderPass, nullptr);
}
for (uint32_t i = 0; i < frameBuffers.size(); i++)
for (auto& frameBuffer : frameBuffers)
{
vkDestroyFramebuffer(device, frameBuffers[i], nullptr);
vkDestroyFramebuffer(device, frameBuffer, nullptr);
}
for (auto& shaderModule : shaderModules)
@ -1509,7 +1504,6 @@ int32_t VulkanExampleBase::handleAppInput(struct android_app* app, AInputEvent*
{
int32_t keyCode = AKeyEvent_getKeyCode((const AInputEvent*)event);
int32_t action = AKeyEvent_getAction((const AInputEvent*)event);
int32_t button = 0;
if (action == AKEY_EVENT_ACTION_UP)
return 0;
@ -1554,7 +1548,7 @@ int32_t VulkanExampleBase::handleAppInput(struct android_app* app, AInputEvent*
void VulkanExampleBase::handleAppCommand(android_app * app, int32_t cmd)
{
assert(app->userData != NULL);
assert(app->userData != nullptr);
VulkanExampleBase* vulkanExample = reinterpret_cast<VulkanExampleBase*>(app->userData);
switch (cmd)
{
@ -1568,7 +1562,7 @@ void VulkanExampleBase::handleAppCommand(android_app * app, int32_t cmd)
break;
case APP_CMD_INIT_WINDOW:
LOGD("APP_CMD_INIT_WINDOW");
if (androidApp->window != NULL)
if (androidApp->window != nullptr)
{
if (vulkanExample->initVulkan()) {
vulkanExample->prepare();
@ -3195,8 +3189,8 @@ void VulkanExampleBase::windowResize()
vkDestroyImage(device, depthStencil.image, nullptr);
vkFreeMemory(device, depthStencil.memory, nullptr);
setupDepthStencil();
for (uint32_t i = 0; i < frameBuffers.size(); i++) {
vkDestroyFramebuffer(device, frameBuffers[i], nullptr);
for (auto& frameBuffer : frameBuffers) {
vkDestroyFramebuffer(device, frameBuffer, nullptr);
}
setupFrameBuffer();

View file

@ -77,8 +77,8 @@ class VulkanExampleBase
{
private:
std::string getWindowTitle() const;
uint32_t destWidth;
uint32_t destHeight;
uint32_t destWidth{};
uint32_t destHeight{};
bool resizing = false;
void handleMouseMove(int32_t x, int32_t y);
void nextFrame();
@ -122,13 +122,13 @@ protected:
// Handle to the device graphics queue that command buffers are submitted to
VkQueue queue{ VK_NULL_HANDLE };
// Depth buffer format (selected during Vulkan initialization)
VkFormat depthFormat;
VkFormat depthFormat{VK_FORMAT_UNDEFINED};
// Command buffer pool
VkCommandPool cmdPool{ VK_NULL_HANDLE };
/** @brief Pipeline stages used to wait at for graphics queue submissions */
VkPipelineStageFlags submitPipelineStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
// Contains command buffers and semaphores to be presented to the queue
VkSubmitInfo submitInfo;
VkSubmitInfo submitInfo{};
// Command buffers used for rendering
std::vector<VkCommandBuffer> drawCmdBuffers;
// Global render pass for frame buffer writes
@ -151,7 +151,7 @@ protected:
VkSemaphore presentComplete;
// Command buffer submission and execution
VkSemaphore renderComplete;
} semaphores;
} semaphores{};
std::vector<VkFence> waitFences;
bool requiresStencil{ false };
public:
@ -170,7 +170,7 @@ public:
vks::Benchmark benchmark;
/** @brief Encapsulated physical and logical vulkan device */
vks::VulkanDevice *vulkanDevice;
vks::VulkanDevice *vulkanDevice{};
/** @brief Example settings that can be changed e.g. by command line arguments */
struct Settings {
@ -234,7 +234,7 @@ public:
struct TouchPos {
int32_t x;
int32_t y;
} touchPos;
} touchPos{};
bool touchDown = false;
double touchTimer = 0.0;
int64_t lastTapTime = 0;

View file

@ -40,7 +40,6 @@ public:
VkDeviceMemory memory{ VK_NULL_HANDLE };
};
Image renderImage;
Image depthStencilRenderImage;
VulkanExample() : VulkanExampleBase()
{
@ -67,7 +66,7 @@ public:
deviceCreatepNextChain = &enabledDynamicRenderingFeaturesKHR;
}
~VulkanExample()
~VulkanExample() override
{
if (device) {
vkDestroyPipeline(device, pipeline, nullptr);
@ -160,7 +159,7 @@ public:
}
// Enable physical device features required for this example
virtual void getEnabledFeatures()
void getEnabledFeatures() override
{
// Enable anisotropic filtering if supported
if (deviceFeatures.samplerAnisotropy) {
@ -174,7 +173,7 @@ public:
model.loadFromFile(getAssetPath() + "models/voyager.gltf", vulkanDevice, queue, glTFLoadingFlags);
}
void buildCommandBuffers()
void buildCommandBuffers() override
{
VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo();
@ -365,7 +364,7 @@ public:
memcpy(uniformBuffer.mapped, &uniformData, sizeof(uniformData));
}
void prepare()
void prepare() override
{
VulkanExampleBase::prepare();
@ -390,7 +389,7 @@ public:
VulkanExampleBase::submitFrame();
}
virtual void render()
void render() override
{
if (!prepared)
return;

View file

@ -70,7 +70,7 @@ public:
deviceCreatepNextChain = &enabledPhysicalDeviceHostImageCopyFeaturesEXT;
}
~VulkanExample()
~VulkanExample() override
{
if (device) {
destroyTextureImage(texture);
@ -82,7 +82,7 @@ public:
}
// Enable physical device features required for this example
virtual void getEnabledFeatures()
void getEnabledFeatures() override
{
// Enable anisotropic filtering if supported
if (deviceFeatures.samplerAnisotropy) {
@ -134,7 +134,6 @@ public:
texture.height = ktxTexture->baseHeight;
texture.mipLevels = ktxTexture->numLevels;
ktx_uint8_t *ktxTextureData = ktxTexture_GetData(ktxTexture);
ktx_size_t ktxTextureSize = ktxTexture_GetSize(ktxTexture);
const VkFormat imageFormat = VK_FORMAT_R8G8B8A8_UNORM;
@ -271,7 +270,7 @@ public:
vkFreeMemory(device, texture.deviceMemory, nullptr);
}
void buildCommandBuffers()
void buildCommandBuffers() override
{
VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo();
@ -371,11 +370,11 @@ public:
VkPipelineMultisampleStateCreateInfo multisampleState = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT, 0);
std::vector<VkDynamicState> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
VkPipelineDynamicStateCreateInfo dynamicState = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables);
std::array<VkPipelineShaderStageCreateInfo,2> shaderStages;
// Shaders
shaderStages[0] = loadShader(getShadersPath() + "texture/texture.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shaderStages[1] = loadShader(getShadersPath() + "texture/texture.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
// Shaders
std::array<VkPipelineShaderStageCreateInfo,2> shaderStages = {
loadShader(getShadersPath() + "texture/texture.vert.spv", VK_SHADER_STAGE_VERTEX_BIT),
loadShader(getShadersPath() + "texture/texture.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT)
};
VkGraphicsPipelineCreateInfo pipelineCreateInfo = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass, 0);
pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState;
@ -413,14 +412,14 @@ public:
plane.loadFromFile(getAssetPath() + "models/plane_z.gltf", vulkanDevice, queue, glTFLoadingFlags);
}
void prepare()
void prepare() override
{
VulkanExampleBase::prepare();
// Get the function pointers required host image copies
vkCopyMemoryToImageEXT = reinterpret_cast<PFN_vkCopyMemoryToImageEXT>(vkGetDeviceProcAddr(device, "vkCopyMemoryToImageEXT"));
vkTransitionImageLayoutEXT = reinterpret_cast<PFN_vkTransitionImageLayoutEXT>(vkGetDeviceProcAddr(device, "vkTransitionImageLayoutEXT"));
vkGetPhysicalDeviceFormatProperties2 = reinterpret_cast<PFN_vkGetPhysicalDeviceFormatProperties2>(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFormatProperties2"));
vkGetPhysicalDeviceFormatProperties2 = reinterpret_cast<PFN_vkGetPhysicalDeviceFormatProperties2>(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFormatProperties2KHR"));
loadAssets();
loadTexture();
@ -440,7 +439,7 @@ public:
VulkanExampleBase::submitFrame();
}
virtual void render()
void render() override
{
if (!prepared)
return;
@ -448,7 +447,7 @@ public:
draw();
}
virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay)
void OnUpdateUIOverlay(vks::UIOverlay *overlay) override
{
if (overlay->header("Settings")) {
if (overlay->sliderFloat("LOD bias", &uniformData.lodBias, 0.0f, (float)texture.mipLevels)) {

View file

@ -99,6 +99,8 @@ public:
camera.setRotation(glm::vec3(0.5f, 210.05f, 0.0f));
camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 256.0f);
ui.subpass = 2;
enabledFeatures.fragmentStoresAndAtomics = VK_TRUE;
}
~VulkanExample()

View file

@ -6,7 +6,7 @@
* Contrary to the other examples, this one won't make use of helper functions or initializers
* Except in a few cases (swap chain setup e.g.)
*
* Copyright (C) 2016-2024 by Sascha Willems - www.saschawillems.de
* Copyright (C) 2016-2025 by Sascha Willems - www.saschawillems.de
*
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/
@ -126,7 +126,7 @@ public:
// Values not set here are initialized in the base class constructor
}
~VulkanExample()
~VulkanExample() override
{
// Clean up used Vulkan resources
// Note: Inherited destructor cleans up resources stored in base class
@ -452,7 +452,7 @@ public:
// Create the depth (and stencil) buffer attachments used by our framebuffers
// Note: Override of virtual function in the base class and called from within VulkanExampleBase::prepare
void setupDepthStencil()
void setupDepthStencil() override
{
// Create an optimal image used as the depth stencil attachment
VkImageCreateInfo imageCI{};
@ -502,7 +502,7 @@ public:
// Create a frame buffer for each swap chain image
// Note: Override of virtual function in the base class and called from within VulkanExampleBase::prepare
void setupFrameBuffer()
void setupFrameBuffer() override
{
// Create a frame buffer for every image in the swapchain
frameBuffers.resize(swapChain.images.size());
@ -533,7 +533,7 @@ public:
// This allows the driver to know up-front what the rendering will look like and is a good opportunity to optimize especially on tile-based renderers (with multiple subpasses)
// Using sub pass dependencies also adds implicit layout transitions for the attachment used, so we don't need to add explicit image memory barriers to transform them
// Note: Override of virtual function in the base class and called from within VulkanExampleBase::prepare
void setupRenderPass()
void setupRenderPass() override
{
// This example will use a single render pass with one subpass
@ -622,7 +622,7 @@ public:
// Vulkan loads its shaders from an immediate binary representation called SPIR-V
// Shaders are compiled offline from e.g. GLSL using the reference glslang compiler
// This function loads such a shader from a binary file and returns a shader module structure
VkShaderModule loadSPIRVShader(std::string filename)
VkShaderModule loadSPIRVShader(const std::string& filename)
{
size_t shaderSize;
char* shaderCode{ nullptr };
@ -886,7 +886,7 @@ public:
}
void prepare()
void prepare() override
{
VulkanExampleBase::prepare();
createSynchronizationPrimitives();
@ -900,7 +900,7 @@ public:
prepared = true;
}
virtual void render()
void render() override
{
if (!prepared)
return;

View file

@ -117,7 +117,7 @@ public:
deviceCreatepNextChain = &enabledFeatures;
}
~VulkanExample()
~VulkanExample() override
{
// Clean up used Vulkan resources
// Note: Inherited destructor cleans up resources stored in base class
@ -140,6 +140,14 @@ public:
}
}
virtual void getEnabledFeatures() override
{
// Vulkan 1.3 device support is required for this example
if (deviceProperties.apiVersion < VK_API_VERSION_1_3) {
vks::tools::exitFatal("Selected GPU does not support support Vulkan 1.3", VK_ERROR_INCOMPATIBLE_DRIVER);
}
}
// This function is used to request a device memory type that supports all the property flags we request (e.g. device local, host visible)
// Upon success it will return the index of the memory type that fits our requested memory properties
// This is necessary as implementations can offer an arbitrary number of memory types with different memory properties
@ -381,7 +389,7 @@ public:
// Create the depth (and stencil) buffer attachments
// While we don't do any depth testing in this sample, having depth testing is very common so it's a good idea to learn it from the very start
void setupDepthStencil()
void setupDepthStencil() override
{
// Create an optimal tiled image used as the depth stencil attachment
VkImageCreateInfo imageCI{ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
@ -428,7 +436,7 @@ public:
// Vulkan loads its shaders from an immediate binary representation called SPIR-V
// Shaders are compiled offline from e.g. GLSL using the reference glslang compiler
// This function loads such a shader from a binary file and returns a shader module structure
VkShaderModule loadSPIRVShader(std::string filename)
VkShaderModule loadSPIRVShader(const std::string& filename)
{
size_t shaderSize;
char* shaderCode{ nullptr };
@ -661,7 +669,7 @@ public:
}
void prepare()
void prepare() override
{
VulkanExampleBase::prepare();
createSynchronizationPrimitives();
@ -673,7 +681,7 @@ public:
prepared = true;
}
virtual void render() override
void render() override
{
// Use a fence to wait until the command buffer has finished execution before using it again
vkWaitForFences(device, 1, &waitFences[currentFrame], VK_TRUE, UINT64_MAX);