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
This commit is contained in:
parent
c598b1e7ab
commit
8b4ee59033
11 changed files with 580 additions and 0 deletions
|
|
@ -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/)<br/>
|
||||
|
||||
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/)
|
||||
|
|
|
|||
35
android/examples/bufferdeviceaddress/CMakeLists.txt
Normal file
35
android/examples/bufferdeviceaddress/CMakeLists.txt
Normal file
|
|
@ -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
|
||||
)
|
||||
71
android/examples/bufferdeviceaddress/build.gradle
Normal file
71
android/examples/bufferdeviceaddress/build.gradle
Normal file
|
|
@ -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
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="de.saschawillems.vulkanBufferDeviceAddress">
|
||||
|
||||
<application
|
||||
android:label="Vulkan buffer device address"
|
||||
android:icon="@drawable/icon"
|
||||
android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
|
||||
<activity android:name="de.saschawillems.vulkanSample.VulkanActivity"
|
||||
android:screenOrientation="landscape"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:exported="true">
|
||||
<meta-data android:name="android.app.lib_name"
|
||||
android:value="native-lib" />
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.gamepad" android:required="false" />
|
||||
|
||||
</manifest>
|
||||
|
|
@ -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) { }
|
||||
}
|
||||
}
|
||||
|
|
@ -85,6 +85,7 @@ endfunction(buildExamples)
|
|||
|
||||
set(EXAMPLES
|
||||
bloom
|
||||
bufferdeviceaddress
|
||||
computecloth
|
||||
computecullandlod
|
||||
computeheadless
|
||||
|
|
|
|||
324
examples/bufferdeviceaddress/bufferdeviceaddress.cpp
Normal file
324
examples/bufferdeviceaddress/bufferdeviceaddress.cpp
Normal file
|
|
@ -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<Cube, 2> 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<VkDescriptorPoolSize> 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<VkDescriptorSetLayoutBinding> 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<VkWriteDescriptorSet> writeDescriptorSets = {
|
||||
vks::initializers::writeDescriptorSet(descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, &texture.descriptor)
|
||||
};
|
||||
vkUpdateDescriptorSets(device, static_cast<uint32_t>(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<VkDynamicState> 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<uint32_t>(dynamicStateEnables.size()), 0);
|
||||
std::array<VkPipelineShaderStageCreateInfo, 2> 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<uint32_t>(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<PFN_vkGetBufferDeviceAddressKHR>(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()
|
||||
20
shaders/glsl/bufferdeviceaddress/cube.frag
Normal file
20
shaders/glsl/bufferdeviceaddress/cube.frag
Normal file
|
|
@ -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);
|
||||
}
|
||||
BIN
shaders/glsl/bufferdeviceaddress/cube.frag.spv
Normal file
BIN
shaders/glsl/bufferdeviceaddress/cube.frag.spv
Normal file
Binary file not shown.
42
shaders/glsl/bufferdeviceaddress/cube.vert
Normal file
42
shaders/glsl/bufferdeviceaddress/cube.vert
Normal file
|
|
@ -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);
|
||||
}
|
||||
BIN
shaders/glsl/bufferdeviceaddress/cube.vert.spv
Normal file
BIN
shaders/glsl/bufferdeviceaddress/cube.vert.spv
Normal file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue