Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • vulkan2021/vkcv-framework
1 result
Show changes
Showing
with 1172 additions and 4 deletions
#pragma once
#include "Upscaling.hpp"
#include <vkcv/ShaderProgram.hpp>
namespace vkcv::upscaling {
enum class FSRQualityMode : int {
NONE = 0,
ULTRA_QUALITY = 1,
QUALITY = 2,
BALANCED = 3,
PERFORMANCE = 4
};
void getFSRResolution(FSRQualityMode mode,
uint32_t outputWidth, uint32_t outputHeight,
uint32_t &inputWidth, uint32_t &inputHeight);
float getFSRLodBias(FSRQualityMode mode);
struct FSRConstants {
uint32_t Const0 [4];
uint32_t Const1 [4];
uint32_t Const2 [4];
uint32_t Const3 [4];
uint32_t Sample [4];
};
class FSRUpscaling : public Upscaling {
private:
PipelineHandle m_easuPipeline;
PipelineHandle m_rcasPipeline;
DescriptorSetHandle m_easuDescriptorSet;
DescriptorSetHandle m_rcasDescriptorSet;
Buffer<FSRConstants> m_easuConstants;
Buffer<FSRConstants> m_rcasConstants;
ImageHandle m_intermediateImage;
SamplerHandle m_sampler;
bool m_hdr;
/**
* Sharpness will calculate the rcasAttenuation value
* which should be between 0.0f and 2.0f (default: 0.25f).
*
* rcasAttenuation = (1.0f - sharpness) * 2.0f
*
* So the default value for sharpness should be 0.875f.
*
* Beware that 0.0f or any negative value of sharpness will
* disable the rcas pass completely.
*/
float m_sharpness;
public:
explicit FSRUpscaling(Core& core);
void recordUpscaling(const CommandStreamHandle& cmdStream,
const ImageHandle& input,
const ImageHandle& output) override;
[[nodiscard]]
bool isHdrEnabled() const;
void setHdrEnabled(bool enabled);
[[nodiscard]]
float getSharpness() const;
void setSharpness(float sharpness);
};
}
#pragma once
#include <vkcv/Core.hpp>
#include <vkcv/Handles.hpp>
namespace vkcv::upscaling {
class Upscaling {
protected:
Core& m_core;
public:
Upscaling(Core& core);
~Upscaling() = default;
virtual void recordUpscaling(const CommandStreamHandle& cmdStream,
const ImageHandle& input,
const ImageHandle& output) = 0;
};
}
Subproject commit bcffc8171efb80e265991301a49670ed755088dd
#include "vkcv/upscaling/BilinearUpscaling.hpp"
namespace vkcv::upscaling {
BilinearUpscaling::BilinearUpscaling(Core &core) : Upscaling(core) {}
void BilinearUpscaling::recordUpscaling(const CommandStreamHandle &cmdStream, const ImageHandle &input,
const ImageHandle &output) {
m_core.recordBlitImage(cmdStream, input, output, SamplerFilterType::LINEAR);
}
}
#include "vkcv/upscaling/FSRUpscaling.hpp"
#include <stdint.h>
#include <math.h>
#define A_CPU 1
#include <ffx_a.h>
#include <ffx_fsr1.h>
#include "ffx_a.h.hxx"
#include "ffx_fsr1.h.hxx"
#include "FSR_Pass.glsl.hxx"
#include <vkcv/File.hpp>
#include <vkcv/Logger.hpp>
#include <vkcv/shader/GLSLCompiler.hpp>
namespace vkcv::upscaling {
void getFSRResolution(FSRQualityMode mode,
uint32_t outputWidth, uint32_t outputHeight,
uint32_t &inputWidth, uint32_t &inputHeight) {
float scale;
switch (mode) {
case FSRQualityMode::ULTRA_QUALITY:
scale = 1.3f;
break;
case FSRQualityMode::QUALITY:
scale = 1.5f;
break;
case FSRQualityMode::BALANCED:
scale = 1.7f;
break;
case FSRQualityMode::PERFORMANCE:
scale = 2.0f;
break;
default:
scale = 1.0f;
break;
}
inputWidth = static_cast<uint32_t>(
std::round(static_cast<float>(outputWidth) / scale)
);
inputHeight = static_cast<uint32_t>(
std::round(static_cast<float>(outputHeight) / scale)
);
}
float getFSRLodBias(FSRQualityMode mode) {
switch (mode) {
case FSRQualityMode::ULTRA_QUALITY:
return -0.38f;
case FSRQualityMode::QUALITY:
return -0.58f;
case FSRQualityMode::BALANCED:
return -0.79f;
case FSRQualityMode::PERFORMANCE:
return -1.0f;
default:
return 0.0f;
}
}
static std::vector<DescriptorBinding> getDescriptorBindings() {
return std::vector<DescriptorBinding>({
DescriptorBinding(
0, DescriptorType::UNIFORM_BUFFER_DYNAMIC,
1, ShaderStage::COMPUTE
),
DescriptorBinding(
1, DescriptorType::IMAGE_SAMPLED,
1, ShaderStage::COMPUTE
),
DescriptorBinding(
2, DescriptorType::IMAGE_STORAGE,
1, ShaderStage::COMPUTE
),
DescriptorBinding(
3, DescriptorType::SAMPLER,
1, ShaderStage::COMPUTE
)
});
}
template<typename T>
bool checkFeatures(const vk::BaseInStructure* base, vk::StructureType type, bool (*check)(const T& features)) {
if (base->sType == type) {
return check(*reinterpret_cast<const T*>(base));
} else
if (base->pNext) {
return checkFeatures<T>(base->pNext, type, check);
} else {
return false;
}
}
static bool checkFloat16(const vk::PhysicalDeviceFloat16Int8FeaturesKHR& features) {
return features.shaderFloat16;
}
static bool check16Storage(const vk::PhysicalDevice16BitStorageFeaturesKHR& features) {
return features.storageBuffer16BitAccess;
}
static bool writeShaderCode(const std::filesystem::path &shaderPath, const std::string& code) {
std::ofstream file (shaderPath.string(), std::ios::out);
if (!file.is_open()) {
vkcv_log(LogLevel::ERROR, "The file could not be opened (%s)", shaderPath.string().c_str());
return false;
}
file.seekp(0);
file.write(code.c_str(), static_cast<std::streamsize>(code.length()));
file.close();
return true;
}
static bool compileFSRShader(vkcv::shader::GLSLCompiler& compiler,
const shader::ShaderCompiledFunction& compiled) {
std::filesystem::path directory = generateTemporaryDirectoryPath();
if (!std::filesystem::create_directory(directory)) {
vkcv_log(LogLevel::ERROR, "The directory could not be created (%s)", directory.string().c_str());
return false;
}
if (!writeShaderCode(directory / "ffx_a.h", FFX_A_H_SHADER)) {
return false;
}
if (!writeShaderCode(directory / "ffx_fsr1.h", FFX_FSR1_H_SHADER)) {
return false;
}
return compiler.compileSource(vkcv::ShaderStage::COMPUTE,
FSR_PASS_GLSL_SHADER.c_str(),
[&directory, &compiled] (vkcv::ShaderStage shaderStage,
const std::filesystem::path& path) {
if (compiled) {
compiled(shaderStage, path);
}
std::filesystem::remove_all(directory);
}, directory
);
}
FSRUpscaling::FSRUpscaling(Core& core) :
Upscaling(core),
m_easuPipeline(),
m_rcasPipeline(),
m_easuDescriptorSet(m_core.createDescriptorSet(getDescriptorBindings())),
m_rcasDescriptorSet(m_core.createDescriptorSet(getDescriptorBindings())),
m_easuConstants(m_core.createBuffer<FSRConstants>(
BufferType::UNIFORM,1,
BufferMemoryType::HOST_VISIBLE
)),
m_rcasConstants(m_core.createBuffer<FSRConstants>(
BufferType::UNIFORM,1,
BufferMemoryType::HOST_VISIBLE
)),
m_intermediateImage(),
m_sampler(m_core.createSampler(
SamplerFilterType::LINEAR,
SamplerFilterType::LINEAR,
SamplerMipmapMode::NEAREST,
SamplerAddressMode::CLAMP_TO_EDGE
)),
m_hdr(false),
m_sharpness(0.875f) {
vkcv::shader::GLSLCompiler easuCompiler;
vkcv::shader::GLSLCompiler rcasCompiler;
const auto& features = m_core.getContext().getPhysicalDevice().getFeatures2();
const bool float16Support = (
checkFeatures<vk::PhysicalDeviceFloat16Int8FeaturesKHR>(
reinterpret_cast<const vk::BaseInStructure*>(&features),
vk::StructureType::ePhysicalDeviceShaderFloat16Int8FeaturesKHR,
checkFloat16
) &&
checkFeatures<vk::PhysicalDevice16BitStorageFeaturesKHR>(
reinterpret_cast<const vk::BaseInStructure*>(&features),
vk::StructureType::ePhysicalDevice16BitStorageFeaturesKHR,
check16Storage
)
) || (true); // check doesn't work because chain is empty
if (!float16Support) {
easuCompiler.setDefine("SAMPLE_SLOW_FALLBACK", "1");
rcasCompiler.setDefine("SAMPLE_SLOW_FALLBACK", "1");
}
easuCompiler.setDefine("SAMPLE_EASU", "1");
rcasCompiler.setDefine("SAMPLE_RCAS", "1");
{
ShaderProgram program;
compileFSRShader(easuCompiler, [&program](vkcv::ShaderStage shaderStage,
const std::filesystem::path& path) {
program.addShader(shaderStage, path);
});
m_easuPipeline = m_core.createComputePipeline(program, {
m_core.getDescriptorSet(m_easuDescriptorSet).layout
});
DescriptorWrites writes;
writes.uniformBufferWrites.emplace_back(
0, m_easuConstants.getHandle(),true
);
writes.samplerWrites.emplace_back(3, m_sampler);
m_core.writeDescriptorSet(m_easuDescriptorSet, writes);
}
{
ShaderProgram program;
compileFSRShader(rcasCompiler, [&program](vkcv::ShaderStage shaderStage,
const std::filesystem::path& path) {
program.addShader(shaderStage, path);
});
m_rcasPipeline = m_core.createComputePipeline(program, {
m_core.getDescriptorSet(m_rcasDescriptorSet).layout
});
DescriptorWrites writes;
writes.uniformBufferWrites.emplace_back(
0, m_rcasConstants.getHandle(),true
);
writes.samplerWrites.emplace_back(3, m_sampler);
m_core.writeDescriptorSet(m_rcasDescriptorSet, writes);
}
}
void FSRUpscaling::recordUpscaling(const CommandStreamHandle& cmdStream,
const ImageHandle& input,
const ImageHandle& output) {
const uint32_t inputWidth = m_core.getImageWidth(input);
const uint32_t inputHeight = m_core.getImageHeight(input);
const uint32_t outputWidth = m_core.getImageWidth(output);
const uint32_t outputHeight = m_core.getImageHeight(output);
if ((!m_intermediateImage) ||
(outputWidth != m_core.getImageWidth(m_intermediateImage)) ||
(outputHeight != m_core.getImageHeight(m_intermediateImage))) {
m_intermediateImage = m_core.createImage(
m_core.getImageFormat(output),
outputWidth, outputHeight,1,
false,
true
).getHandle();
m_core.prepareImageForStorage(cmdStream, m_intermediateImage);
}
const bool rcasEnabled = (
(m_sharpness > +0.0f) &&
((inputWidth < outputWidth) || (inputHeight < outputHeight))
);
{
FSRConstants consts = {};
FsrEasuCon(
consts.Const0, consts.Const1, consts.Const2, consts.Const3,
static_cast<AF1>(inputWidth), static_cast<AF1>(inputHeight),
static_cast<AF1>(inputWidth), static_cast<AF1>(inputHeight),
static_cast<AF1>(outputWidth), static_cast<AF1>(outputHeight)
);
consts.Sample[0] = (((m_hdr) && (!rcasEnabled)) ? 1 : 0);
m_easuConstants.fill(&consts);
}
static const uint32_t threadGroupWorkRegionDim = 16;
uint32_t dispatch[3];
dispatch[0] = (outputWidth + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
dispatch[1] = (outputHeight + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
dispatch[2] = 1;
m_core.recordBufferMemoryBarrier(cmdStream, m_easuConstants.getHandle());
if (rcasEnabled) {
{
DescriptorWrites writes;
writes.sampledImageWrites.emplace_back(1, input);
writes.storageImageWrites.emplace_back(2, m_intermediateImage);
m_core.writeDescriptorSet(m_easuDescriptorSet, writes);
}
{
DescriptorWrites writes;
writes.sampledImageWrites.emplace_back(1, m_intermediateImage);
writes.storageImageWrites.emplace_back(2, output);
m_core.writeDescriptorSet(m_rcasDescriptorSet, writes);
}
m_core.recordComputeDispatchToCmdStream(
cmdStream,
m_easuPipeline,
dispatch,
{DescriptorSetUsage(0, m_core.getDescriptorSet(
m_easuDescriptorSet
).vulkanHandle, { 0 })},
PushConstants(0)
);
{
FSRConstants consts = {};
FsrRcasCon(consts.Const0, (1.0f - m_sharpness) * 2.0f);
consts.Sample[0] = (m_hdr ? 1 : 0);
m_rcasConstants.fill(&consts);
}
m_core.recordBufferMemoryBarrier(cmdStream, m_rcasConstants.getHandle());
m_core.prepareImageForSampling(cmdStream, m_intermediateImage);
m_core.recordComputeDispatchToCmdStream(
cmdStream,
m_rcasPipeline,
dispatch,
{DescriptorSetUsage(0, m_core.getDescriptorSet(
m_rcasDescriptorSet
).vulkanHandle, { 0 })},
PushConstants(0)
);
m_core.prepareImageForStorage(cmdStream, m_intermediateImage);
} else {
{
DescriptorWrites writes;
writes.sampledImageWrites.emplace_back(1, input);
writes.storageImageWrites.emplace_back(2, output);
m_core.writeDescriptorSet(m_easuDescriptorSet, writes);
}
m_core.recordComputeDispatchToCmdStream(
cmdStream,
m_easuPipeline,
dispatch,
{DescriptorSetUsage(0, m_core.getDescriptorSet(
m_easuDescriptorSet
).vulkanHandle, { 0 })},
PushConstants(0)
);
}
}
bool FSRUpscaling::isHdrEnabled() const {
return m_hdr;
}
void FSRUpscaling::setHdrEnabled(bool enabled) {
m_hdr = enabled;
}
float FSRUpscaling::getSharpness() const {
return m_sharpness;
}
void FSRUpscaling::setSharpness(float sharpness) {
m_sharpness = (sharpness < 0.0f ? 0.0f : (sharpness > 1.0f ? 1.0f : sharpness));
}
}
#include "vkcv/upscaling/Upscaling.hpp"
namespace vkcv::upscaling {
Upscaling::Upscaling(Core &core) : m_core(core) {}
}
......@@ -5,3 +5,4 @@ add_subdirectory(first_mesh)
add_subdirectory(first_scene)
add_subdirectory(particle_simulation)
add_subdirectory(voxelization)
add_subdirectory(mesh_shader)
first_triangle
\ No newline at end of file
cmake_minimum_required(VERSION 3.16)
project(mesh_shader)
# setting c++ standard for the project
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# this should fix the execution path to load local files from the project
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
# adding source files to the project
add_executable(mesh_shader src/main.cpp)
target_sources(mesh_shader PRIVATE)
# this should fix the execution path to load local files from the project (for MSVC)
if(MSVC)
set_target_properties(mesh_shader PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
set_target_properties(mesh_shader PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
# in addition to setting the output directory, the working directory has to be set
# by default visual studio sets the working directory to the build directory, when using the debugger
set_target_properties(mesh_shader PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
endif()
# including headers of dependencies and the VkCV framework
target_include_directories(mesh_shader SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_testing_include} ${vkcv_camera_include} ${vkcv_meshlet_include} ${vkcv_shader_compiler_include} ${vkcv_gui_include})
# linking with libraries from all dependencies and the VkCV framework
target_link_libraries(mesh_shader vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_testing vkcv_camera vkcv_meshlet vkcv_shader_compiler vkcv_gui)
\ No newline at end of file
File added
File added
struct ObjectMatrices{
mat4 model;
mat4 mvp;
};
\ No newline at end of file
struct Meshlet{
uint vertexOffset;
uint vertexCount;
uint indexOffset;
uint indexCount;
vec3 meanPosition;
float boundingSphereRadius;
};
\ No newline at end of file
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout(location = 0) in vec3 passNormal;
layout(location = 1) in flat uint passTaskIndex;
layout(location = 0) out vec3 outColor;
uint lowbias32(uint x)
{
x ^= x >> 16;
x *= 0x7feb352dU;
x ^= x >> 15;
x *= 0x846ca68bU;
x ^= x >> 16;
return x;
}
float hashToFloat(uint hash){
return (hash % 255) / 255.f;
}
vec3 colorFromIndex(uint i){
return vec3(
hashToFloat(lowbias32(i+0)),
hashToFloat(lowbias32(i+1)),
hashToFloat(lowbias32(i+2)));
}
void main() {
outColor = normalize(passNormal) * 0.5 + 0.5;
outColor = colorFromIndex(passTaskIndex);
}
\ No newline at end of file
#version 460
#extension GL_ARB_separate_shader_objects : enable
#extension GL_GOOGLE_include_directive : enable
#extension GL_NV_mesh_shader : require
#include "meshlet.inc"
layout(local_size_x=32) in;
layout(triangles) out;
layout(max_vertices=64, max_primitives=126) out;
layout(location = 0) out vec3 passNormal[];
layout(location = 1) out uint passTaskIndex[];
struct Vertex
{
vec3 position; float padding0;
vec3 normal; float padding1;
};
layout(std430, binding = 0) readonly buffer vertexBuffer
{
Vertex vertices[];
};
layout(std430, binding = 1) readonly buffer indexBuffer
{
uint localIndices[]; // breaks for 16 bit indices
};
layout(std430, binding = 2) readonly buffer meshletBuffer
{
Meshlet meshlets[];
};
taskNV in Task {
uint meshletIndices[32];
mat4 mvp;
} IN;
void main() {
uint meshletIndex = IN.meshletIndices[gl_WorkGroupID.x];
Meshlet meshlet = meshlets[meshletIndex];
// set vertices
for(uint i = 0; i < 2; i++){
uint workIndex = gl_LocalInvocationID.x + 32 * i;
if(workIndex >= meshlet.vertexCount){
break;
}
uint vertexIndex = meshlet.vertexOffset + workIndex;
Vertex vertex = vertices[vertexIndex];
gl_MeshVerticesNV[workIndex].gl_Position = IN.mvp * vec4(vertex.position, 1);
passNormal[workIndex] = vertex.normal;
passTaskIndex[workIndex] = meshletIndex;
}
// set local indices
for(uint i = 0; i < 12; i++){
uint workIndex = gl_LocalInvocationID.x + i * 32;
if(workIndex >= meshlet.indexCount){
break;
}
uint indexBufferIndex = meshlet.indexOffset + workIndex;
gl_PrimitiveIndicesNV[workIndex] = localIndices[indexBufferIndex];
}
if(gl_LocalInvocationID.x == 0){
gl_PrimitiveCountNV = meshlet.indexCount / 3;
}
}
\ No newline at end of file
#version 460
#extension GL_ARB_separate_shader_objects : enable
#extension GL_NV_mesh_shader : require
#extension GL_GOOGLE_include_directive : enable
#include "meshlet.inc"
#include "common.inc"
layout(local_size_x=32) in;
taskNV out Task {
uint meshletIndices[32];
mat4 mvp;
} OUT;
layout( push_constant ) uniform constants{
uint matrixIndex;
uint meshletCount;
};
// TODO: reuse mesh stage binding at location 2 after required fix in framework
layout(std430, binding = 5) readonly buffer meshletBuffer
{
Meshlet meshlets[];
};
struct Plane{
vec3 pointOnPlane;
float padding0;
vec3 normal;
float padding1;
};
layout(set=0, binding=3, std140) uniform cameraPlaneBuffer{
Plane cameraPlanes[6];
};
layout(std430, binding = 4) readonly buffer matrixBuffer
{
ObjectMatrices objectMatrices[];
};
shared uint taskCount;
bool isSphereInsideFrustum(vec3 spherePos, float sphereRadius, Plane cameraPlanes[6]){
bool isInside = true;
for(int i = 0; i < 6; i++){
Plane p = cameraPlanes[i];
isInside = isInside && dot(p.normal, spherePos - p.pointOnPlane) - sphereRadius < 0;
}
return isInside;
}
void main() {
if(gl_LocalInvocationID.x >= meshletCount){
return;
}
uint meshletIndex = gl_GlobalInvocationID.x;
Meshlet meshlet = meshlets[meshletIndex];
if(gl_LocalInvocationID.x == 0){
taskCount = 0;
}
// TODO: scaling support
vec3 meshletPositionWorld = (vec4(meshlet.meanPosition, 1) * objectMatrices[matrixIndex].model).xyz;
if(isSphereInsideFrustum(meshletPositionWorld, meshlet.boundingSphereRadius, cameraPlanes)){
uint outIndex = atomicAdd(taskCount, 1);
OUT.meshletIndices[outIndex] = gl_GlobalInvocationID.x;
}
if(gl_LocalInvocationID.x == 0){
gl_TaskCountNV = taskCount;
OUT.mvp = objectMatrices[matrixIndex].mvp;
}
}
\ No newline at end of file
#version 450
#extension GL_ARB_separate_shader_objects : enable
#extension GL_GOOGLE_include_directive : enable
#include "common.inc"
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inNormal;
layout(location = 0) out vec3 passNormal;
layout(location = 1) out uint dummyOutput;
layout(std430, binding = 0) readonly buffer matrixBuffer
{
ObjectMatrices objectMatrices[];
};
layout( push_constant ) uniform constants{
uint matrixIndex;
uint padding; // pad to same size as mesh shader constants
};
void main() {
gl_Position = objectMatrices[matrixIndex].mvp * vec4(inPosition, 1.0);
passNormal = inNormal;
dummyOutput = padding * 0; // padding must be used, else compiler shrinks constant size
}
\ No newline at end of file
#include <iostream>
#include <vkcv/Core.hpp>
#include <GLFW/glfw3.h>
#include <vkcv/camera/CameraManager.hpp>
#include <chrono>
#include <vkcv/shader/GLSLCompiler.hpp>
#include <vkcv/gui/GUI.hpp>
#include <vkcv/asset/asset_loader.hpp>
#include <vkcv/meshlet/Meshlet.hpp>
#include <vkcv/meshlet/Tipsify.hpp>
#include <vkcv/meshlet/Forsyth.hpp>
struct Plane {
glm::vec3 pointOnPlane;
float padding0;
glm::vec3 normal;
float padding1;
};
struct CameraPlanes {
Plane planes[6];
};
CameraPlanes computeCameraPlanes(const vkcv::camera::Camera& camera) {
const float fov = camera.getFov();
const glm::vec3 pos = camera.getPosition();
const float ratio = camera.getRatio();
const glm::vec3 forward = glm::normalize(camera.getFront());
float near;
float far;
camera.getNearFar(near, far);
glm::vec3 up = glm::vec3(0, -1, 0);
glm::vec3 right = glm::normalize(glm::cross(forward, up));
up = glm::cross(forward, right);
const glm::vec3 nearCenter = pos + forward * near;
const glm::vec3 farCenter = pos + forward * far;
const float tanFovHalf = glm::tan(fov / 2);
const glm::vec3 nearUpCenter = nearCenter + up * tanFovHalf * near;
const glm::vec3 nearDownCenter = nearCenter - up * tanFovHalf * near;
const glm::vec3 nearRightCenter = nearCenter + right * tanFovHalf * near * ratio;
const glm::vec3 nearLeftCenter = nearCenter - right * tanFovHalf * near * ratio;
const glm::vec3 farUpCenter = farCenter + up * tanFovHalf * far;
const glm::vec3 farDownCenter = farCenter - up * tanFovHalf * far;
const glm::vec3 farRightCenter = farCenter + right * tanFovHalf * far * ratio;
const glm::vec3 farLeftCenter = farCenter - right * tanFovHalf * far * ratio;
CameraPlanes cameraPlanes;
// near
cameraPlanes.planes[0].pointOnPlane = nearCenter;
cameraPlanes.planes[0].normal = -forward;
// far
cameraPlanes.planes[1].pointOnPlane = farCenter;
cameraPlanes.planes[1].normal = forward;
// top
cameraPlanes.planes[2].pointOnPlane = nearUpCenter;
cameraPlanes.planes[2].normal = glm::normalize(glm::cross(farUpCenter - nearUpCenter, right));
// bot
cameraPlanes.planes[3].pointOnPlane = nearDownCenter;
cameraPlanes.planes[3].normal = glm::normalize(glm::cross(right, farDownCenter - nearDownCenter));
// right
cameraPlanes.planes[4].pointOnPlane = nearRightCenter;
cameraPlanes.planes[4].normal = glm::normalize(glm::cross(up, farRightCenter - nearRightCenter));
// left
cameraPlanes.planes[5].pointOnPlane = nearLeftCenter;
cameraPlanes.planes[5].normal = glm::normalize(glm::cross(farLeftCenter - nearLeftCenter, up));
return cameraPlanes;
}
int main(int argc, const char** argv) {
const char* applicationName = "Mesh shader";
const int windowWidth = 1280;
const int windowHeight = 720;
vkcv::Window window = vkcv::Window::create(
applicationName,
windowWidth,
windowHeight,
false
);
vkcv::Core core = vkcv::Core::create(
window,
applicationName,
VK_MAKE_VERSION(0, 0, 1),
{ vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute },
{},
{ "VK_KHR_swapchain", VK_NV_MESH_SHADER_EXTENSION_NAME }
);
vkcv::gui::GUI gui (core, window);
const auto& context = core.getContext();
const vk::Instance& instance = context.getInstance();
const vk::PhysicalDevice& physicalDevice = context.getPhysicalDevice();
const vk::Device& device = context.getDevice();
vkcv::asset::Scene mesh;
const char* path = argc > 1 ? argv[1] : "resources/Bunny/Bunny.glb";
vkcv::asset::loadScene(path, mesh);
assert(!mesh.vertexGroups.empty());
auto vertexBuffer = core.createBuffer<uint8_t>(
vkcv::BufferType::VERTEX,
mesh.vertexGroups[0].vertexBuffer.data.size(),
vkcv::BufferMemoryType::DEVICE_LOCAL
);
vertexBuffer.fill(mesh.vertexGroups[0].vertexBuffer.data);
auto indexBuffer = core.createBuffer<uint8_t>(
vkcv::BufferType::INDEX,
mesh.vertexGroups[0].indexBuffer.data.size(),
vkcv::BufferMemoryType::DEVICE_LOCAL
);
indexBuffer.fill(mesh.vertexGroups[0].indexBuffer.data);
// format data for mesh shader
auto& attributes = mesh.vertexGroups[0].vertexBuffer.attributes;
std::sort(attributes.begin(), attributes.end(), [](const vkcv::asset::VertexAttribute& x, const vkcv::asset::VertexAttribute& y) {
return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type);
});
const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = {
vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[0].offset), vertexBuffer.getVulkanHandle()),
vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[1].offset), vertexBuffer.getVulkanHandle()),
vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[2].offset), vertexBuffer.getVulkanHandle()) };
const auto& bunny = mesh.vertexGroups[0];
std::vector<vkcv::meshlet::Vertex> interleavedVertices = vkcv::meshlet::convertToVertices(bunny.vertexBuffer.data, bunny.numVertices, attributes[0], attributes[1]);
// mesh shader buffers
const auto& assetLoaderIndexBuffer = mesh.vertexGroups[0].indexBuffer;
std::vector<uint32_t> indexBuffer32Bit = vkcv::meshlet::assetLoaderIndicesTo32BitIndices(assetLoaderIndexBuffer.data, assetLoaderIndexBuffer.type);
vkcv::meshlet::VertexCacheReorderResult tipsifyResult = vkcv::meshlet::tipsifyMesh(indexBuffer32Bit, interleavedVertices.size());
vkcv::meshlet::VertexCacheReorderResult forsythResult = vkcv::meshlet::forsythReorder(indexBuffer32Bit, interleavedVertices.size());
const auto meshShaderModelData = createMeshShaderModelData(interleavedVertices, forsythResult.indexBuffer, forsythResult.skippedIndices);
auto meshShaderVertexBuffer = core.createBuffer<vkcv::meshlet::Vertex>(
vkcv::BufferType::STORAGE,
meshShaderModelData.vertices.size());
meshShaderVertexBuffer.fill(meshShaderModelData.vertices);
auto meshShaderIndexBuffer = core.createBuffer<uint32_t>(
vkcv::BufferType::STORAGE,
meshShaderModelData.localIndices.size());
meshShaderIndexBuffer.fill(meshShaderModelData.localIndices);
auto meshletBuffer = core.createBuffer<vkcv::meshlet::Meshlet>(
vkcv::BufferType::STORAGE,
meshShaderModelData.meshlets.size(),
vkcv::BufferMemoryType::DEVICE_LOCAL
);
meshletBuffer.fill(meshShaderModelData.meshlets);
// attachments
const vkcv::AttachmentDescription present_color_attachment(
vkcv::AttachmentOperation::STORE,
vkcv::AttachmentOperation::CLEAR,
core.getSwapchain().getFormat());
const vkcv::AttachmentDescription depth_attachment(
vkcv::AttachmentOperation::STORE,
vkcv::AttachmentOperation::CLEAR,
vk::Format::eD32Sfloat
);
vkcv::PassConfig bunnyPassDefinition({ present_color_attachment, depth_attachment });
vkcv::PassHandle renderPass = core.createPass(bunnyPassDefinition);
if (!renderPass)
{
std::cout << "Error. Could not create renderpass. Exiting." << std::endl;
return EXIT_FAILURE;
}
vkcv::ShaderProgram bunnyShaderProgram{};
vkcv::shader::GLSLCompiler compiler;
compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/shader.vert"),
[&bunnyShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
bunnyShaderProgram.addShader(shaderStage, path);
});
compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/shader.frag"),
[&bunnyShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
bunnyShaderProgram.addShader(shaderStage, path);
});
const std::vector<vkcv::VertexAttachment> vertexAttachments = bunnyShaderProgram.getVertexAttachments();
std::vector<vkcv::VertexBinding> bindings;
for (size_t i = 0; i < vertexAttachments.size(); i++) {
bindings.push_back(vkcv::VertexBinding(i, { vertexAttachments[i] }));
}
const vkcv::VertexLayout bunnyLayout (bindings);
vkcv::DescriptorSetHandle vertexShaderDescriptorSet = core.createDescriptorSet(bunnyShaderProgram.getReflectedDescriptors()[0]);
const vkcv::PipelineConfig bunnyPipelineDefinition {
bunnyShaderProgram,
(uint32_t)windowWidth,
(uint32_t)windowHeight,
renderPass,
{ bunnyLayout },
{ core.getDescriptorSet(vertexShaderDescriptorSet).layout },
false
};
struct ObjectMatrices {
glm::mat4 model;
glm::mat4 mvp;
};
const size_t objectCount = 1;
vkcv::Buffer<ObjectMatrices> matrixBuffer = core.createBuffer<ObjectMatrices>(vkcv::BufferType::STORAGE, objectCount);
vkcv::DescriptorWrites vertexShaderDescriptorWrites;
vertexShaderDescriptorWrites.storageBufferWrites = { vkcv::BufferDescriptorWrite(0, matrixBuffer.getHandle()) };
core.writeDescriptorSet(vertexShaderDescriptorSet, vertexShaderDescriptorWrites);
vkcv::PipelineHandle bunnyPipeline = core.createGraphicsPipeline(bunnyPipelineDefinition);
if (!bunnyPipeline)
{
std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl;
return EXIT_FAILURE;
}
// mesh shader
vkcv::ShaderProgram meshShaderProgram;
compiler.compile(vkcv::ShaderStage::TASK, std::filesystem::path("resources/shaders/shader.task"),
[&meshShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
meshShaderProgram.addShader(shaderStage, path);
});
compiler.compile(vkcv::ShaderStage::MESH, std::filesystem::path("resources/shaders/shader.mesh"),
[&meshShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
meshShaderProgram.addShader(shaderStage, path);
});
compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/shader.frag"),
[&meshShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) {
meshShaderProgram.addShader(shaderStage, path);
});
uint32_t setID = 0;
vkcv::DescriptorSetHandle meshShaderDescriptorSet = core.createDescriptorSet( meshShaderProgram.getReflectedDescriptors()[setID]);
const vkcv::VertexLayout meshShaderLayout(bindings);
const vkcv::PipelineConfig meshShaderPipelineDefinition{
meshShaderProgram,
(uint32_t)windowWidth,
(uint32_t)windowHeight,
renderPass,
{meshShaderLayout},
{core.getDescriptorSet(meshShaderDescriptorSet).layout},
false
};
vkcv::PipelineHandle meshShaderPipeline = core.createGraphicsPipeline(meshShaderPipelineDefinition);
if (!meshShaderPipeline)
{
std::cout << "Error. Could not create mesh shader pipeline. Exiting." << std::endl;
return EXIT_FAILURE;
}
vkcv::Buffer<CameraPlanes> cameraPlaneBuffer = core.createBuffer<CameraPlanes>(vkcv::BufferType::UNIFORM, 1);
vkcv::DescriptorWrites meshShaderWrites;
meshShaderWrites.storageBufferWrites = {
vkcv::BufferDescriptorWrite(0, meshShaderVertexBuffer.getHandle()),
vkcv::BufferDescriptorWrite(1, meshShaderIndexBuffer.getHandle()),
vkcv::BufferDescriptorWrite(2, meshletBuffer.getHandle()),
vkcv::BufferDescriptorWrite(4, matrixBuffer.getHandle()),
vkcv::BufferDescriptorWrite(5, meshletBuffer.getHandle()),
};
meshShaderWrites.uniformBufferWrites = {
vkcv::BufferDescriptorWrite(3, cameraPlaneBuffer.getHandle()),
};
core.writeDescriptorSet( meshShaderDescriptorSet, meshShaderWrites);
vkcv::ImageHandle depthBuffer = core.createImage(vk::Format::eD32Sfloat, windowWidth, windowHeight, 1, false).getHandle();
auto start = std::chrono::system_clock::now();
vkcv::ImageHandle swapchainImageHandle = vkcv::ImageHandle::createSwapchainImageHandle();
const vkcv::Mesh renderMesh(vertexBufferBindings, indexBuffer.getVulkanHandle(), mesh.vertexGroups[0].numIndices, vkcv::IndexBitCount::Bit32);
const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
vkcv::camera::CameraManager cameraManager(window);
uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT);
cameraManager.getCamera(camIndex0).setPosition(glm::vec3(0, 0, -2));
bool useMeshShader = true;
bool updateFrustumPlanes = true;
while (window.isWindowOpen())
{
vkcv::Window::pollEvents();
uint32_t swapchainWidth, swapchainHeight; // No resizing = No problem
if (!core.beginFrame(swapchainWidth, swapchainHeight)) {
continue;
}
auto end = std::chrono::system_clock::now();
auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
start = end;
cameraManager.update(0.000001 * static_cast<double>(deltatime.count()));
const vkcv::camera::Camera& camera = cameraManager.getActiveCamera();
ObjectMatrices objectMatrices;
objectMatrices.model = *reinterpret_cast<glm::mat4*>(&mesh.meshes.front().modelMatrix);
objectMatrices.mvp = camera.getMVP() * objectMatrices.model;
matrixBuffer.fill({ objectMatrices });
struct PushConstants {
uint32_t matrixIndex;
uint32_t meshletCount;
};
PushConstants pushConstants{ 0, static_cast<uint32_t>(meshShaderModelData.meshlets.size()) };
if (updateFrustumPlanes) {
const CameraPlanes cameraPlanes = computeCameraPlanes(camera);
cameraPlaneBuffer.fill({ cameraPlanes });
}
const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer };
auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
vkcv::PushConstants pushConstantData(sizeof(pushConstants));
pushConstantData.appendDrawcall(pushConstants);
if (useMeshShader) {
vkcv::DescriptorSetUsage descriptorUsage(0, core.getDescriptorSet(meshShaderDescriptorSet).vulkanHandle);
const uint32_t taskCount = (meshShaderModelData.meshlets.size() + 31) / 32;
core.recordMeshShaderDrawcalls(
cmdStream,
renderPass,
meshShaderPipeline,
pushConstantData,
{ vkcv::MeshShaderDrawcall({descriptorUsage}, taskCount)},
{ renderTargets });
}
else {
vkcv::DescriptorSetUsage descriptorUsage(0, core.getDescriptorSet(vertexShaderDescriptorSet).vulkanHandle);
core.recordDrawcallsToCmdStream(
cmdStream,
renderPass,
bunnyPipeline,
pushConstantData,
{ vkcv::DrawcallInfo(renderMesh, { descriptorUsage }) },
{ renderTargets });
}
core.prepareSwapchainImageForPresent(cmdStream);
core.submitCommandStream(cmdStream);
gui.beginGUI();
ImGui::Begin("Settings");
ImGui::Checkbox("Use mesh shader", &useMeshShader);
ImGui::Checkbox("Update frustum culling", &updateFrustumPlanes);
ImGui::End();
gui.endGUI();
core.endFrame();
}
return 0;
}
......@@ -263,6 +263,10 @@ void BloomAndFlares::execWholePipeline(const vkcv::CommandStreamHandle &cmdStrea
void BloomAndFlares::updateImageDimensions(uint32_t width, uint32_t height)
{
if ((width == m_Width) && (height == m_Height)) {
return;
}
m_Width = width;
m_Height = height;
......
......@@ -163,13 +163,13 @@ int main(int argc, const char **argv) {
particleBuffer.fill(particleSystem.getParticles());
vkcv::DescriptorWrites setWrites;
setWrites.uniformBufferWrites = {vkcv::UniformBufferDescriptorWrite(0,color.getHandle()),
vkcv::UniformBufferDescriptorWrite(1,position.getHandle())};
setWrites.storageBufferWrites = { vkcv::StorageBufferDescriptorWrite(2,particleBuffer.getHandle())};
setWrites.uniformBufferWrites = {vkcv::BufferDescriptorWrite(0,color.getHandle()),
vkcv::BufferDescriptorWrite(1,position.getHandle())};
setWrites.storageBufferWrites = { vkcv::BufferDescriptorWrite(2,particleBuffer.getHandle())};
core.writeDescriptorSet(descriptorSet, setWrites);
vkcv::DescriptorWrites computeWrites;
computeWrites.storageBufferWrites = { vkcv::StorageBufferDescriptorWrite(0,particleBuffer.getHandle())};
computeWrites.storageBufferWrites = { vkcv::BufferDescriptorWrite(0,particleBuffer.getHandle())};
core.writeDescriptorSet(computeDescriptorSet, computeWrites);
if (!particlePipeline || !computePipeline)
......