From 8b4ee5903364c86bc4d572b53ee8f1ac80501851 Mon Sep 17 00:00:00 2001 From: Sascha Willems Date: Wed, 3 Jul 2024 22:04:57 +0200 Subject: [PATCH] Add new sample for buffer device address (#1144) * Started work on buffer device address sample * Code cleanup * Added BDA sample to readm * Added android build files for BDA sample * Replaces all uniform buffers with references Comments and code cleanup --- README.md | 4 + .../bufferdeviceaddress/CMakeLists.txt | 35 ++ .../examples/bufferdeviceaddress/build.gradle | 71 ++++ .../src/main/AndroidManifest.xml | 25 ++ .../vulkanSample/VulkanActivity.java | 58 ++++ examples/CMakeLists.txt | 1 + .../bufferdeviceaddress.cpp | 324 ++++++++++++++++++ shaders/glsl/bufferdeviceaddress/cube.frag | 20 ++ .../glsl/bufferdeviceaddress/cube.frag.spv | Bin 0 -> 848 bytes shaders/glsl/bufferdeviceaddress/cube.vert | 42 +++ .../glsl/bufferdeviceaddress/cube.vert.spv | Bin 0 -> 2212 bytes 11 files changed, 580 insertions(+) create mode 100644 android/examples/bufferdeviceaddress/CMakeLists.txt create mode 100644 android/examples/bufferdeviceaddress/build.gradle create mode 100644 android/examples/bufferdeviceaddress/src/main/AndroidManifest.xml create mode 100644 android/examples/bufferdeviceaddress/src/main/java/de/saschawillems/vulkanSample/VulkanActivity.java create mode 100644 examples/bufferdeviceaddress/bufferdeviceaddress.cpp create mode 100644 shaders/glsl/bufferdeviceaddress/cube.frag create mode 100644 shaders/glsl/bufferdeviceaddress/cube.frag.spv create mode 100644 shaders/glsl/bufferdeviceaddress/cube.vert create mode 100644 shaders/glsl/bufferdeviceaddress/cube.vert.spv diff --git a/README.md b/README.md index c7ee3c99..1bb9d471 100644 --- a/README.md +++ b/README.md @@ -474,6 +474,10 @@ Basic sample showing how to use shader objects that can be used to replace pipel Shows how to do host image copies, which heavily simplify the host to device image process by fully skipping the staging process. +#### [Buffer device address (VK_KHR_buffer_device_addres)](./examples/bufferdeviceaddress/)
+ +Demonstrates the use of virtual GPU addresses to directly access buffer data in shader. Instead of e.g. using descriptors to access uniforms, with this extension you simply provide an address to the memory you want to read from in the shader and that address can be arbitrarily changed e.g. via a push constant. + ### Misc #### [Vulkan Gears](examples/gears/) diff --git a/android/examples/bufferdeviceaddress/CMakeLists.txt b/android/examples/bufferdeviceaddress/CMakeLists.txt new file mode 100644 index 00000000..6fd13dda --- /dev/null +++ b/android/examples/bufferdeviceaddress/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.4.1 FATAL_ERROR) + +set(NAME bufferdeviceaddress) + +set(SRC_DIR ../../../examples/${NAME}) +set(BASE_DIR ../../../base) +set(EXTERNAL_DIR ../../../external) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -DVK_USE_PLATFORM_ANDROID_KHR -DVK_NO_PROTOTYPES") + +file(GLOB EXAMPLE_SRC "${SRC_DIR}/*.cpp") + +add_library(native-lib SHARED ${EXAMPLE_SRC}) + +add_library(native-app-glue STATIC ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) + +add_subdirectory(../base ${CMAKE_SOURCE_DIR}/../base) + +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") + +include_directories(${BASE_DIR}) +include_directories(${EXTERNAL_DIR}) +include_directories(${EXTERNAL_DIR}/glm) +include_directories(${EXTERNAL_DIR}/imgui) +include_directories(${EXTERNAL_DIR}/tinygltf) +include_directories(${ANDROID_NDK}/sources/android/native_app_glue) + +target_link_libraries( + native-lib + native-app-glue + libbase + android + log + z +) diff --git a/android/examples/bufferdeviceaddress/build.gradle b/android/examples/bufferdeviceaddress/build.gradle new file mode 100644 index 00000000..9fe9a2af --- /dev/null +++ b/android/examples/bufferdeviceaddress/build.gradle @@ -0,0 +1,71 @@ +apply plugin: 'com.android.application' +apply from: '../gradle/outputfilename.gradle' + +android { + compileSdkVersion rootProject.ext.compileSdkVersion + defaultConfig { + applicationId "de.saschawillems.vulkanBufferDeviceAddress" + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + versionCode 1 + versionName "1.0" + ndk { + abiFilters rootProject.ext.abiFilters + } + externalNativeBuild { + cmake { + cppFlags "-std=c++14" + arguments "-DANDROID_STL=c++_shared", '-DANDROID_TOOLCHAIN=clang' + } + } + } + sourceSets { + main.assets.srcDirs = ['assets'] + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + externalNativeBuild { + cmake { + path "CMakeLists.txt" + } + } +} + +task copyTask { + copy { + from '../../common/res/drawable' + into "src/main/res/drawable" + include 'icon.png' + } + + copy { + from rootProject.ext.shaderPath + 'glsl/base' + into 'assets/shaders/glsl/base' + include '*.spv' + } + + copy { + from rootProject.ext.shaderPath + 'glsl/bufferdeviceaddress' + into 'assets/shaders/glsl/bufferdeviceaddress' + include '*.*' + } + + copy { + from rootProject.ext.assetPath + 'models' + into 'assets/models' + include 'cube.gltf' + } + + copy { + from rootProject.ext.assetPath + 'textures' + into 'assets/textures' + include 'crate01_color_height_rgba.ktx' + } + +} + +preBuild.dependsOn copyTask \ No newline at end of file diff --git a/android/examples/bufferdeviceaddress/src/main/AndroidManifest.xml b/android/examples/bufferdeviceaddress/src/main/AndroidManifest.xml new file mode 100644 index 00000000..e67632e4 --- /dev/null +++ b/android/examples/bufferdeviceaddress/src/main/AndroidManifest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + diff --git a/android/examples/bufferdeviceaddress/src/main/java/de/saschawillems/vulkanSample/VulkanActivity.java b/android/examples/bufferdeviceaddress/src/main/java/de/saschawillems/vulkanSample/VulkanActivity.java new file mode 100644 index 00000000..12e14fc6 --- /dev/null +++ b/android/examples/bufferdeviceaddress/src/main/java/de/saschawillems/vulkanSample/VulkanActivity.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2018 by Sascha Willems - www.saschawillems.de + * + * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + */ +package de.saschawillems.vulkanSample; + +import android.app.AlertDialog; +import android.app.NativeActivity; +import android.content.DialogInterface; +import android.content.pm.ApplicationInfo; +import android.os.Bundle; + +import java.util.concurrent.Semaphore; + +public class VulkanActivity extends NativeActivity { + + static { + // Load native library + System.loadLibrary("native-lib"); + } + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + // Use a semaphore to create a modal dialog + + private final Semaphore semaphore = new Semaphore(0, true); + + public void showAlert(final String message) + { + final VulkanActivity activity = this; + + ApplicationInfo applicationInfo = activity.getApplicationInfo(); + final String applicationName = applicationInfo.nonLocalizedLabel.toString(); + + this.runOnUiThread(new Runnable() { + public void run() { + AlertDialog.Builder builder = new AlertDialog.Builder(activity, android.R.style.Theme_Material_Dialog_Alert); + builder.setTitle(applicationName); + builder.setMessage(message); + builder.setPositiveButton("Close", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + semaphore.release(); + } + }); + builder.setCancelable(false); + AlertDialog dialog = builder.create(); + dialog.show(); + } + }); + try { + semaphore.acquire(); + } + catch (InterruptedException e) { } + } +} diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index e39d82d9..3e95b6cd 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -85,6 +85,7 @@ endfunction(buildExamples) set(EXAMPLES bloom + bufferdeviceaddress computecloth computecullandlod computeheadless diff --git a/examples/bufferdeviceaddress/bufferdeviceaddress.cpp b/examples/bufferdeviceaddress/bufferdeviceaddress.cpp new file mode 100644 index 00000000..223f7b33 --- /dev/null +++ b/examples/bufferdeviceaddress/bufferdeviceaddress.cpp @@ -0,0 +1,324 @@ +/* +* Vulkan Example - Buffer device address +* +* This sample shows how to read data from a buffer device address (aka "reference") instead of using uniforms +* The application passes buffer device addresses to the shader via push constants, and the shader then simply reads the data behind that address +* See cube.vert for the shader side of things +* +* Copyright (C) 2024 by Sascha Willems - www.saschawillems.de +* +*/ + +#include "vulkanexamplebase.h" +#include "VulkanglTFModel.h" + +class VulkanExample : public VulkanExampleBase +{ +public: + bool animate = true; + + struct Cube { + glm::mat4 modelMatrix; + vks::Buffer buffer; + glm::vec3 rotation; + VkDeviceAddress bufferDeviceAddress{}; + }; + std::array cubes{}; + + vks::Texture2D texture; + vkglTF::Model model; + + // Global matrices + struct Scene { + glm::mat4 mvp; + vks::Buffer buffer; + VkDeviceAddress bufferDeviceAddress{}; + } scene; + + VkPipeline pipeline{ VK_NULL_HANDLE }; + VkPipelineLayout pipelineLayout{ VK_NULL_HANDLE }; + VkDescriptorSet descriptorSet{ VK_NULL_HANDLE }; + VkDescriptorSetLayout descriptorSetLayout{ VK_NULL_HANDLE }; + + PFN_vkGetBufferDeviceAddressKHR vkGetBufferDeviceAddressKHR{ VK_NULL_HANDLE }; + VkPhysicalDeviceBufferDeviceAddressFeatures enabledBufferDeviceAddresFeatures{}; + + // This sample passes the buffer references ("pointer") using push constants, the shader then reads data from that buffer address + struct PushConstantBlock { + // Reference to the global matrices + VkDeviceAddress sceneReference; + // Reference to the per model matrices + VkDeviceAddress modelReference; + }; + + VulkanExample() : VulkanExampleBase() + { + title = "Buffer device address"; + camera.type = Camera::CameraType::lookat; + camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 512.0f); + camera.setRotation(glm::vec3(0.0f, 0.0f, 0.0f)); + camera.setTranslation(glm::vec3(0.0f, 0.0f, -5.0f)); + + enabledInstanceExtensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + enabledInstanceExtensions.push_back(VK_KHR_DEVICE_GROUP_CREATION_EXTENSION_NAME); + enabledDeviceExtensions.push_back(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME); + enabledDeviceExtensions.push_back(VK_KHR_DEVICE_GROUP_EXTENSION_NAME); + + enabledBufferDeviceAddresFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES; + enabledBufferDeviceAddresFeatures.bufferDeviceAddress = VK_TRUE; + + deviceCreatepNextChain = &enabledBufferDeviceAddresFeatures; + } + + ~VulkanExample() + { + if (device) { + vkDestroyPipeline(device, pipeline, nullptr); + vkDestroyPipelineLayout(device, pipelineLayout, nullptr); + vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); + texture.destroy(); + for (auto cube : cubes) { + cube.buffer.destroy(); + } + scene.buffer.destroy(); + } + } + + virtual void getEnabledFeatures() + { + if (deviceFeatures.samplerAnisotropy) { + enabledFeatures.samplerAnisotropy = VK_TRUE; + }; + } + + void loadAssets() + { + const uint32_t glTFLoadingFlags = vkglTF::FileLoadingFlags::PreTransformVertices | vkglTF::FileLoadingFlags::PreMultiplyVertexColors | vkglTF::FileLoadingFlags::FlipY; + model.loadFromFile(getAssetPath() + "models/cube.gltf", vulkanDevice, queue, glTFLoadingFlags); + texture.loadFromFile(getAssetPath() + "textures/crate01_color_height_rgba.ktx", VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue); + } + + // We pass all data via buffer device addresses, so we only allocate descriptors for the images + void setupDescriptors() + { + // Pool + std::vector descriptorPoolSizes = { + vks::initializers::descriptorPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1) + }; + VkDescriptorPoolCreateInfo descriptorPoolInfo = vks::initializers::descriptorPoolCreateInfo(descriptorPoolSizes, 2); + VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolInfo, nullptr, &descriptorPool)); + + // Layout + std::vector setLayoutBindings = { + vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 0) + }; + VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings); + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout)); + + // Set + VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayout, 1); + VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSet)); + + std::vector writeDescriptorSets = { + vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, &texture.descriptor) + }; + vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr); + } + + void preparePipelines() + { + // The buffer addresses will be passed to the shader using push constants + // That way it's very easy to do a draw call, change the reference to another buffer (or part of that buffer) and do the next draw call using different data + VkPushConstantRange pushConstantRange{}; + pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + pushConstantRange.offset = 0; + pushConstantRange.size = sizeof(PushConstantBlock); + + VkPipelineLayoutCreateInfo pipelineLayoutCI = vks::initializers::pipelineLayoutCreateInfo(); + pipelineLayoutCI.pushConstantRangeCount = 1; + pipelineLayoutCI.pPushConstantRanges = &pushConstantRange; + pipelineLayoutCI.setLayoutCount = 1; + pipelineLayoutCI.pSetLayouts = &descriptorSetLayout; + VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelineLayout)); + + const std::vector dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + + VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE); + VkPipelineRasterizationStateCreateInfo rasterizationStateCI = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_BACK_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE, 0); + VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE); + VkPipelineColorBlendStateCreateInfo colorBlendStateCI = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState); + VkPipelineDepthStencilStateCreateInfo depthStencilStateCI = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL); + VkPipelineViewportStateCreateInfo viewportStateCI = vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0); + VkPipelineMultisampleStateCreateInfo multisampleStateCI = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT, 0); + VkPipelineDynamicStateCreateInfo dynamicStateCI = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables.data(), static_cast(dynamicStateEnables.size()), 0); + std::array shaderStages = { + loadShader(getShadersPath() + "bufferdeviceaddress/cube.vert.spv", VK_SHADER_STAGE_VERTEX_BIT), + loadShader(getShadersPath() + "bufferdeviceaddress/cube.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT) + }; + + VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass, 0); + pipelineCI.pInputAssemblyState = &inputAssemblyStateCI; + pipelineCI.pRasterizationState = &rasterizationStateCI; + pipelineCI.pColorBlendState = &colorBlendStateCI; + pipelineCI.pMultisampleState = &multisampleStateCI; + pipelineCI.pViewportState = &viewportStateCI; + pipelineCI.pDepthStencilState = &depthStencilStateCI; + pipelineCI.pDynamicState = &dynamicStateCI; + pipelineCI.stageCount = static_cast(shaderStages.size()); + pipelineCI.pStages = shaderStages.data(); + pipelineCI.pVertexInputState = vkglTF::Vertex::getPipelineVertexInputState({ vkglTF::VertexComponent::Position, vkglTF::VertexComponent::Normal, vkglTF::VertexComponent::UV, vkglTF::VertexComponent::Color }); + VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); + } + + void prepareBuffers() + { + // Note that we don't use this buffer for uniforms but rather pass it's address as a reference to the shader, so isntead of the uniform buffer usage we use a different flag + VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &scene.buffer, sizeof(glm::mat4))); + VK_CHECK_RESULT(scene.buffer.map()); + + // Get the device of this buffer that is later on passed to the shader (aka "reference") + VkBufferDeviceAddressInfo bufferDeviceAdressInfo{}; + bufferDeviceAdressInfo.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO; + bufferDeviceAdressInfo.buffer = scene.buffer.buffer; + scene.bufferDeviceAddress = vkGetBufferDeviceAddressKHR(device, &bufferDeviceAdressInfo); + + for (auto& cube : cubes) { + // Note that we don't use this buffer for uniforms but rather pass it's address as a reference to the shader, so isntead of the uniform buffer usage we use a different flag + VK_CHECK_RESULT(vulkanDevice->createBuffer(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &cube.buffer, sizeof(glm::mat4))); + VK_CHECK_RESULT(cube.buffer.map()); + + // Get the device of this buffer that is later on passed to the shader (aka "reference") + bufferDeviceAdressInfo.buffer = cube.buffer.buffer; + cube.bufferDeviceAddress = vkGetBufferDeviceAddressKHR(device, &bufferDeviceAdressInfo); + } + updateBuffers(); + } + + void updateBuffers() + { + scene.mvp = camera.matrices.perspective * camera.matrices.view; + memcpy(scene.buffer.mapped, &scene, sizeof(glm::mat4)); + + cubes[0].modelMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(-2.0f, 0.0f, 0.0f)); + cubes[1].modelMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(1.5f, 0.5f, 0.0f)); + + for (auto& cube : cubes) { + cube.modelMatrix = glm::rotate(cube.modelMatrix, glm::radians(cube.rotation.x), glm::vec3(1.0f, 0.0f, 0.0f)); + cube.modelMatrix = glm::rotate(cube.modelMatrix, glm::radians(cube.rotation.y), glm::vec3(0.0f, 1.0f, 0.0f)); + cube.modelMatrix = glm::rotate(cube.modelMatrix, glm::radians(cube.rotation.z), glm::vec3(0.0f, 0.0f, 1.0f)); + cube.modelMatrix = glm::scale(cube.modelMatrix, glm::vec3(0.25f)); + memcpy(cube.buffer.mapped, &cube.modelMatrix, sizeof(glm::mat4)); + } + } + + void prepare() + { + VulkanExampleBase::prepare(); + + // We need this extension function to get the address of a buffer so we can pass it to the shader + vkGetBufferDeviceAddressKHR = reinterpret_cast(vkGetDeviceProcAddr(device, "vkGetBufferDeviceAddressKHR")); + + loadAssets(); + prepareBuffers(); + setupDescriptors(); + preparePipelines(); + buildCommandBuffers(); + prepared = true; + } + + void buildCommandBuffers() + { + VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); + + VkClearValue clearValues[2]; + clearValues[0].color = defaultClearColor; + clearValues[1].depthStencil = { 1.0f, 0 }; + + VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo(); + renderPassBeginInfo.renderPass = renderPass; + renderPassBeginInfo.renderArea.offset.x = 0; + renderPassBeginInfo.renderArea.offset.y = 0; + renderPassBeginInfo.renderArea.extent.width = width; + renderPassBeginInfo.renderArea.extent.height = height; + renderPassBeginInfo.clearValueCount = 2; + renderPassBeginInfo.pClearValues = clearValues; + + for (int32_t i = 0; i < drawCmdBuffers.size(); ++i) { + renderPassBeginInfo.framebuffer = frameBuffers[i]; + + VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo)); + + vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + + VkViewport viewport = vks::initializers::viewport((float)width, (float)height, 0.0f, 1.0f); + vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); + + VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0); + vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); + + vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); + + model.bindBuffers(drawCmdBuffers[i]); + + // Instead of using descriptors to pass global and per-model matrices to the shader, we can now simply pass buffer references via push constants + // The reader then simply reads data from the address of that reference + PushConstantBlock references{}; + // Pass pointer to the global matrix via a buffer device address + references.sceneReference = scene.bufferDeviceAddress; + + for (auto& cube : cubes) { + // Pass pointer to this cube's data buffer via a buffer device address + // So instead of having to bind different descriptors, we only pass a different device address + // This doesn't have to be an address from a different buffer, but could very well be just another address in the same buffer + references.modelReference = cube.bufferDeviceAddress; + vkCmdPushConstants(drawCmdBuffers[i], pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(PushConstantBlock), &references); + + model.draw(drawCmdBuffers[i]); + } + + drawUI(drawCmdBuffers[i]); + + vkCmdEndRenderPass(drawCmdBuffers[i]); + + VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i])); + } + } + + void draw() + { + VulkanExampleBase::prepareFrame(); + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; + VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); + VulkanExampleBase::submitFrame(); + } + + virtual void render() + { + if (!prepared) + return; + draw(); + if (animate && !paused) { + cubes[0].rotation.x += 2.5f * frameTimer; + if (cubes[0].rotation.x > 360.0f) + cubes[0].rotation.x -= 360.0f; + cubes[1].rotation.y += 2.0f * frameTimer; + if (cubes[1].rotation.x > 360.0f) + cubes[1].rotation.x -= 360.0f; + } + if ((camera.updated) || (animate && !paused)) { + updateBuffers(); + } + } + + virtual void OnUpdateUIOverlay(vks::UIOverlay* overlay) + { + if (overlay->header("Settings")) { + overlay->checkBox("Animate", &animate); + } + } +}; + +VULKAN_EXAMPLE_MAIN() diff --git a/shaders/glsl/bufferdeviceaddress/cube.frag b/shaders/glsl/bufferdeviceaddress/cube.frag new file mode 100644 index 00000000..0feb8ede --- /dev/null +++ b/shaders/glsl/bufferdeviceaddress/cube.frag @@ -0,0 +1,20 @@ +/* Copyright (c) 2024, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +#version 450 + +layout (set = 0, binding = 0) uniform sampler2D samplerColorMap; + +layout (location = 0) in vec3 inNormal; +layout (location = 1) in vec3 inColor; +layout (location = 2) in vec2 inUV; + +layout (location = 0) out vec4 outFragColor; + +void main() +{ + outFragColor = texture(samplerColorMap, inUV) * vec4(inColor, 1.0); +} \ No newline at end of file diff --git a/shaders/glsl/bufferdeviceaddress/cube.frag.spv b/shaders/glsl/bufferdeviceaddress/cube.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..92a0dfa3a0c4e9fcc9bafd453423250510fee3cb GIT binary patch literal 848 zcmYk3OG^S_6oyagm}Ys&(#$Se%RpLG1W_%5+*lM*tp|!2aAst*eVhJRzp72p`^=^M z;CSY|=lRa%GoRaEG+VHWm2B6N)3BTrlU9uVaP%-5OlL2HqvJz}s^w!4re{#;BCc;<%B_F{ix za#_8mD7b#SX>zBf@1nGxtSz2*AZGWTeoOMaDEe6%Gb36@(=N%w({KJ{aaUwT2{`I4 zrw|(Th}n@t|C&7Ym=nG(Pc3wKVs=J@Uz3Mt9{6>6a_I2Hyb0QYV%QCt*~ev;=Y7bZ z=s%$^yK|`dEya;Pm&(jl8S@e|)2fVlo<;FSO&JV240E()%y2J;{+(2l`_nd4jX5%| kn`+E-r|Q(-l5roNJiC0Ry|?vEf0zqR&;Nw>qwP**zjaqEUjP6A literal 0 HcmV?d00001 diff --git a/shaders/glsl/bufferdeviceaddress/cube.vert b/shaders/glsl/bufferdeviceaddress/cube.vert new file mode 100644 index 00000000..0bf5c602 --- /dev/null +++ b/shaders/glsl/bufferdeviceaddress/cube.vert @@ -0,0 +1,42 @@ +/* Copyright (c) 2024, Sascha Willems + * + * SPDX-License-Identifier: MIT + * + */ + +#version 450 + +#extension GL_EXT_scalar_block_layout: require +#extension GL_EXT_buffer_reference : require + +layout (location = 0) in vec3 inPos; +layout (location = 1) in vec3 inNormal; +layout (location = 2) in vec2 inUV; +layout (location = 3) in vec3 inColor; + +layout (buffer_reference, scalar) readonly buffer MatrixReference { + mat4 matrix; +}; + +layout (push_constant) uniform PushConstants +{ + // Pointer to the buffer with the scene's MVP matrix + MatrixReference sceneDataReference; + // Pointer to the buffer for the data for each model + MatrixReference modelDataReference; +} pushConstants; + +layout (location = 0) out vec3 outNormal; +layout (location = 1) out vec3 outColor; +layout (location = 2) out vec2 outUV; + +void main() +{ + MatrixReference sceneData = pushConstants.sceneDataReference; + MatrixReference modelData = pushConstants.modelDataReference; + + outNormal = inNormal; + outColor = inColor; + outUV = inUV; + gl_Position = sceneData.matrix * modelData.matrix * vec4(inPos.xyz, 1.0); +} \ No newline at end of file diff --git a/shaders/glsl/bufferdeviceaddress/cube.vert.spv b/shaders/glsl/bufferdeviceaddress/cube.vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..55d16de378431c0e11e8b443eac560028d9f4928 GIT binary patch literal 2212 zcmZ9N+fLL_6oxkpGn^DeQ9tzwK}|9Q!nF4JkYHnyEN zYn#sD!Aas*-M~rGD0cTfXXj{d&x;jb%3><(TkA7Px;ryF_ke6YreJ<42l;x}g6rX}-|f__@H|4@Q9({3v3&g)Mf@~TekskRqZJ=r>}Ez$AZ z*x3o9>Srf#PoksLn4&zI>lSt1F6Djerm=s#*~n9pCp@c`*@!KgbHYj03%yl0b)^%} zZ8naQgOw;uQa4N!;gZf#zh#sEY5Inpe$=8}FCbr&oxHuW9iGX{83vkTq{GoaUGQBL z*W4iI=%*hxsLYT4%5NnKqFAc9wyfWNSf{}BUOpeUb31eJ!wky5WTu4o182jFx4k&^ zjs?Zmd30=1;-`KTifzyQxo5`vVY=mi@$j=Y<|^At;2*B~bg=3*Y=zu*6a)=hQE!2F z!Q#BU4?k2%?Q6nzZpAZrIK!uN9T#S;DT|HKM_?{UDyjj;+0dE$AH83E?OdDV;kYAQ zhlCh78_&#$ujqWtYE;4kGbb?dU}n^e%Q>bO*qIOZdhWLDJF%GaF&|cUeap@aM}9bV z>qibaa$61^j_-tf5Nm7EYihCj#lnxthZyupX(jEyb=N`Z^s_7(Qa1VmChxF*cWQ?N zkII*R!0={C7-nq zO2?nGU)D~2?uq}ET!*I~n7LZ}h;(AD9n75JtUq(cAB?>$!A`v!+NnoRsW+}2UwHh% zyaPDwx3m*$?cj-=a|cIv^6ste5A&I=ntoqO7;iJfzRr*jVb zed)}eUfO%{W~f75YAi^M`MQk%3u)w|*1X;>_vJmX;Qxx@J0=%zfbWnT7V}+!U&)IZ z_Dh&Mvt3dQ^VD%N{)I9U54Qe8((z~3)}Ox<@c5G-jE!0H_L(C#`yE@`nD}yTW42&y z%w$r+Jcwg%U|VlSI_Kg{^pAg-aP>QsvR*#SdHTa!wy}?-b4DBcSUOyD?5r@njs31V z%x6wQUhMV`Poy)i_wu2yPbF|H%RQ40$4o7^m~+(FP$ctwE+HO&?9Ba}@?u+(P#+)8 V&O2C^@Rm3`eFEe6Pn8xV{{hy{xM%